added common debug output function nxlog_debug
[public/netxms.git] / src / libnetxms / log.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Utility Library
4 ** Copyright (C) 2003-2016 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
26 #if HAVE_SYSLOG_H
27 #include <syslog.h>
28 #endif
29
30 #define MAX_LOG_HISTORY_SIZE 128
31
32 /**
33 * Static data
34 */
35 #ifdef _WIN32
36 static HANDLE m_eventLogHandle = NULL;
37 static HMODULE m_msgModuleHandle = NULL;
38 #else
39 static unsigned int m_numMessages;
40 static const TCHAR **m_messages;
41 #endif
42 static TCHAR m_logFileName[MAX_PATH] = _T("");
43 static FILE *m_logFileHandle = NULL;
44 static MUTEX m_mutexLogAccess = INVALID_MUTEX_HANDLE;
45 static UINT32 m_flags = 0;
46 static int s_debugLevel = 0;
47 static DWORD s_debugMsg = 0;
48 static int m_rotationMode = NXLOG_ROTATION_BY_SIZE;
49 static int m_maxLogSize = 4096 * 1024; // 4 MB
50 static int m_logHistorySize = 4; // Keep up 4 previous log files
51 static TCHAR m_dailyLogSuffixTemplate[64] = _T("%Y%m%d");
52 static time_t m_currentDayStart = 0;
53 static NxLogConsoleWriter m_consoleWriter = (NxLogConsoleWriter)_tprintf;
54 static String s_logBuffer;
55 static THREAD s_writerThread = INVALID_THREAD_HANDLE;
56 static CONDITION s_writerStopCondition = INVALID_CONDITION_HANDLE;
57
58 /**
59 * Set debug level
60 */
61 void LIBNETXMS_EXPORTABLE nxlog_set_debug_level(int level)
62 {
63 s_debugLevel = level;
64 }
65
66 /**
67 * Get current debug level
68 */
69 int LIBNETXMS_EXPORTABLE nxlog_get_debug_level()
70 {
71 return s_debugLevel;
72 }
73
74 /**
75 * Format current time for output
76 */
77 static TCHAR *FormatLogTimestamp(TCHAR *buffer)
78 {
79 INT64 now = GetCurrentTimeMs();
80 time_t t = now / 1000;
81 #if HAVE_LOCALTIME_R
82 struct tm ltmBuffer;
83 struct tm *loc = localtime_r(&t, &ltmBuffer);
84 #else
85 struct tm *loc = localtime(&t);
86 #endif
87 _tcsftime(buffer, 32, _T("[%d-%b-%Y %H:%M:%S"), loc);
88 _sntprintf(&buffer[21], 8, _T(".%03d]"), (int)(now % 1000));
89 return buffer;
90 }
91
92 /**
93 * Set timestamp of start of the current day
94 */
95 static void SetDayStart()
96 {
97 time_t now = time(NULL);
98 struct tm dayStart;
99 #if HAVE_LOCALTIME_R
100 localtime_r(&now, &dayStart);
101 #else
102 struct tm *ltm = localtime(&now);
103 memcpy(&dayStart, ltm, sizeof(struct tm));
104 #endif
105 dayStart.tm_hour = 0;
106 dayStart.tm_min = 0;
107 dayStart.tm_sec = 0;
108 m_currentDayStart = mktime(&dayStart);
109 }
110
111 /**
112 * Set log rotation policy
113 * Setting log size to 0 or mode to NXLOG_ROTATION_DISABLED disables log rotation
114 */
115 bool LIBNETXMS_EXPORTABLE nxlog_set_rotation_policy(int rotationMode, int maxLogSize, int historySize, const TCHAR *dailySuffix)
116 {
117 bool isValid = true;
118
119 if ((rotationMode >= 0) || (rotationMode <= 2))
120 {
121 m_rotationMode = rotationMode;
122 if (rotationMode == NXLOG_ROTATION_BY_SIZE)
123 {
124 if ((maxLogSize == 0) || (maxLogSize >= 1024))
125 {
126 m_maxLogSize = maxLogSize;
127 }
128 else
129 {
130 m_maxLogSize = 1024;
131 isValid = false;
132 }
133
134 if ((historySize >= 0) && (historySize <= MAX_LOG_HISTORY_SIZE))
135 {
136 m_logHistorySize = historySize;
137 }
138 else
139 {
140 if (historySize > MAX_LOG_HISTORY_SIZE)
141 m_logHistorySize = MAX_LOG_HISTORY_SIZE;
142 isValid = false;
143 }
144 }
145 else if (rotationMode == NXLOG_ROTATION_DAILY)
146 {
147 if ((dailySuffix != NULL) && (dailySuffix[0] != 0))
148 nx_strncpy(m_dailyLogSuffixTemplate, dailySuffix, sizeof(m_dailyLogSuffixTemplate) / sizeof(TCHAR));
149 SetDayStart();
150 }
151 }
152 else
153 {
154 isValid = false;
155 }
156
157 return isValid;
158 }
159
160 /**
161 * Set console writer
162 */
163 void LIBNETXMS_EXPORTABLE nxlog_set_console_writer(NxLogConsoleWriter writer)
164 {
165 m_consoleWriter = writer;
166 }
167
168 /**
169 * Rotate log
170 */
171 static bool RotateLog(bool needLock)
172 {
173 int i;
174 TCHAR oldName[MAX_PATH], newName[MAX_PATH];
175
176 if (m_flags & NXLOG_USE_SYSLOG)
177 return FALSE; // Cannot rotate system logs
178
179 if (needLock)
180 MutexLock(m_mutexLogAccess);
181
182 if ((m_logFileHandle != NULL) && (m_flags & NXLOG_IS_OPEN))
183 {
184 fclose(m_logFileHandle);
185 m_flags &= ~NXLOG_IS_OPEN;
186 }
187
188 if (m_rotationMode == NXLOG_ROTATION_BY_SIZE)
189 {
190 // Delete old files
191 for(i = MAX_LOG_HISTORY_SIZE; i >= m_logHistorySize; i--)
192 {
193 _sntprintf(oldName, MAX_PATH, _T("%s.%d"), m_logFileName, i);
194 _tunlink(oldName);
195 }
196
197 // Shift file names
198 for(; i >= 0; i--)
199 {
200 _sntprintf(oldName, MAX_PATH, _T("%s.%d"), m_logFileName, i);
201 _sntprintf(newName, MAX_PATH, _T("%s.%d"), m_logFileName, i + 1);
202 _trename(oldName, newName);
203 }
204
205 // Rename current log to name.0
206 _sntprintf(newName, MAX_PATH, _T("%s.0"), m_logFileName);
207 _trename(m_logFileName, newName);
208 }
209 else if (m_rotationMode == NXLOG_ROTATION_DAILY)
210 {
211 #if HAVE_LOCALTIME_R
212 struct tm ltmBuffer;
213 struct tm *loc = localtime_r(&m_currentDayStart, &ltmBuffer);
214 #else
215 struct tm *loc = localtime(&m_currentDayStart);
216 #endif
217 TCHAR buffer[64];
218 _tcsftime(buffer, 64, m_dailyLogSuffixTemplate, loc);
219
220 // Rename current log to name.suffix
221 _sntprintf(newName, MAX_PATH, _T("%s.%s"), m_logFileName, buffer);
222 _trename(m_logFileName, newName);
223
224 SetDayStart();
225 }
226
227 // Reopen log
228 #if HAVE_FOPEN64
229 m_logFileHandle = _tfopen64(m_logFileName, _T("w"));
230 #else
231 m_logFileHandle = _tfopen(m_logFileName, _T("w"));
232 #endif
233 if (m_logFileHandle != NULL)
234 {
235 m_flags |= NXLOG_IS_OPEN;
236 TCHAR buffer[32];
237 _ftprintf(m_logFileHandle, _T("%s Log file truncated.\n"), FormatLogTimestamp(buffer));
238 fflush(m_logFileHandle);
239 }
240
241 if (needLock)
242 MutexUnlock(m_mutexLogAccess);
243
244 return (m_flags & NXLOG_IS_OPEN) ? true : false;
245 }
246
247 /**
248 * API for application to force log rotation
249 */
250 bool LIBNETXMS_EXPORTABLE nxlog_rotate()
251 {
252 return RotateLog(true);
253 }
254
255 /**
256 * Background writer thread
257 */
258 static THREAD_RESULT THREAD_CALL BackgroundWriterThread(void *arg)
259 {
260 bool stop = false;
261 while(!stop)
262 {
263 stop = ConditionWait(s_writerStopCondition, 1000);
264
265 // Check for new day start
266 time_t t = time(NULL);
267 if ((m_rotationMode == NXLOG_ROTATION_DAILY) && (t >= m_currentDayStart + 86400))
268 {
269 RotateLog(FALSE);
270 }
271
272 MutexLock(m_mutexLogAccess);
273 if (!s_logBuffer.isEmpty())
274 {
275 if (m_flags & NXLOG_PRINT_TO_STDOUT)
276 m_consoleWriter(_T("%s"), s_logBuffer.getBuffer());
277
278 char *data = s_logBuffer.getUTF8String();
279 s_logBuffer.clear();
280 MutexUnlock(m_mutexLogAccess);
281
282 #ifdef _WIN32
283 fwrite(data, 1, strlen(data), m_logFileHandle);
284 #else
285 // write is used here because on linux fwrite is not working
286 // after calling fwprintf on a stream
287 size_t size = strlen(data);
288 size_t offset = 0;
289 do
290 {
291 int bw = write(fileno(m_logFileHandle), &data[offset], size);
292 if (bw < 0)
293 break;
294 size -= bw;
295 offset += bw;
296 } while(size > 0);
297 #endif
298 free(data);
299
300 // Check log size
301 if ((m_logFileHandle != NULL) && (m_rotationMode == NXLOG_ROTATION_BY_SIZE) && (m_maxLogSize != 0))
302 {
303 struct stat st;
304
305 fstat(fileno(m_logFileHandle), &st);
306 if (st.st_size >= m_maxLogSize)
307 RotateLog(FALSE);
308 }
309 }
310 else
311 {
312 MutexUnlock(m_mutexLogAccess);
313 }
314 }
315 return THREAD_OK;
316 }
317
318 /**
319 * Initialize log
320 */
321 bool LIBNETXMS_EXPORTABLE nxlog_open(const TCHAR *logName, UINT32 flags,
322 const TCHAR *msgModule, unsigned int msgCount, const TCHAR **messages, DWORD debugMsg)
323 {
324 m_flags = flags & 0x7FFFFFFF;
325 #ifdef _WIN32
326 m_msgModuleHandle = GetModuleHandle(msgModule);
327 #else
328 m_numMessages = msgCount;
329 m_messages = messages;
330 #endif
331 s_debugMsg = debugMsg;
332
333 if (m_flags & NXLOG_USE_SYSLOG)
334 {
335 #ifdef _WIN32
336 m_eventLogHandle = RegisterEventSource(NULL, logName);
337 if (m_eventLogHandle != NULL)
338 m_flags |= NXLOG_IS_OPEN;
339 #else
340 #ifdef UNICODE
341 char *mbBuffer;
342
343 mbBuffer = MBStringFromWideString(logName);
344 openlog(mbBuffer, LOG_PID, LOG_DAEMON);
345 free(mbBuffer);
346 #else
347 openlog(logName, LOG_PID, LOG_DAEMON);
348 #endif
349 m_flags |= NXLOG_IS_OPEN;
350 #endif
351 }
352 else
353 {
354 TCHAR buffer[32];
355
356 nx_strncpy(m_logFileName, logName, MAX_PATH);
357 #if HAVE_FOPEN64
358 m_logFileHandle = _tfopen64(logName, _T("a"));
359 #else
360 m_logFileHandle = _tfopen(logName, _T("a"));
361 #endif
362 if (m_logFileHandle != NULL)
363 {
364 m_flags |= NXLOG_IS_OPEN;
365 _ftprintf(m_logFileHandle, _T("\n%s Log file opened\n"), FormatLogTimestamp(buffer));
366 fflush(m_logFileHandle);
367 if (m_flags & NXLOG_BACKGROUND_WRITER)
368 {
369 s_logBuffer.setAllocationStep(8192);
370 s_writerStopCondition = ConditionCreate(TRUE);
371 s_writerThread = ThreadCreateEx(BackgroundWriterThread, 0, NULL);
372 }
373 }
374
375 m_mutexLogAccess = MutexCreate();
376 SetDayStart();
377 }
378 return (m_flags & NXLOG_IS_OPEN) ? true : false;
379 }
380
381 /**
382 * Close log
383 */
384 void LIBNETXMS_EXPORTABLE nxlog_close()
385 {
386 if (m_flags & NXLOG_IS_OPEN)
387 {
388 if (m_flags & NXLOG_USE_SYSLOG)
389 {
390 #ifdef _WIN32
391 DeregisterEventSource(m_eventLogHandle);
392 #else
393 closelog();
394 #endif
395 }
396 else
397 {
398 if (m_flags & NXLOG_BACKGROUND_WRITER)
399 {
400 ConditionSet(s_writerStopCondition);
401 ThreadJoin(s_writerThread);
402 ConditionDestroy(s_writerStopCondition);
403 }
404 if (m_logFileHandle != NULL)
405 fclose(m_logFileHandle);
406 if (m_mutexLogAccess != INVALID_MUTEX_HANDLE)
407 MutexDestroy(m_mutexLogAccess);
408 }
409 m_flags &= ~NXLOG_IS_OPEN;
410 }
411 }
412
413 /**
414 * Write record to log file
415 */
416 static void WriteLogToFile(TCHAR *message, const WORD wType)
417 {
418 TCHAR buffer[64];
419 TCHAR loglevel[64];
420
421 switch(wType)
422 {
423 case EVENTLOG_ERROR_TYPE:
424 _sntprintf(loglevel, 16, _T("[%s] "), _T("ERROR"));
425 break;
426 case EVENTLOG_WARNING_TYPE:
427 _sntprintf(loglevel, 16, _T("[%s] "), _T("WARN "));
428 break;
429 case EVENTLOG_INFORMATION_TYPE:
430 _sntprintf(loglevel, 16, _T("[%s] "), _T("INFO "));
431 break;
432 case EVENTLOG_DEBUG_TYPE:
433 _sntprintf(loglevel, 16, _T("[%s] "), _T("DEBUG"));
434 break;
435 default:
436 _tcsncpy(loglevel, _T(""), 1);
437 break;
438 }
439
440 if (m_flags & NXLOG_BACKGROUND_WRITER)
441 {
442 MutexLock(m_mutexLogAccess);
443
444 FormatLogTimestamp(buffer);
445 s_logBuffer.append(buffer);
446 s_logBuffer.append(_T(" "));
447 s_logBuffer.append(loglevel);
448 s_logBuffer.append(message);
449
450 MutexUnlock(m_mutexLogAccess);
451 }
452 else
453 {
454 // Prevent simultaneous write to log file
455 MutexLock(m_mutexLogAccess);
456
457 // Check for new day start
458 time_t t = time(NULL);
459 if ((m_rotationMode == NXLOG_ROTATION_DAILY) && (t >= m_currentDayStart + 86400))
460 {
461 RotateLog(FALSE);
462 }
463
464 FormatLogTimestamp(buffer);
465 if (m_logFileHandle != NULL)
466 {
467 _ftprintf(m_logFileHandle, _T("%s %s%s"), buffer, loglevel, message);
468 fflush(m_logFileHandle);
469 }
470 if (m_flags & NXLOG_PRINT_TO_STDOUT)
471 m_consoleWriter(_T("%s %s%s"), buffer, loglevel, message);
472
473 // Check log size
474 if ((m_logFileHandle != NULL) && (m_rotationMode == NXLOG_ROTATION_BY_SIZE) && (m_maxLogSize != 0))
475 {
476 struct stat st;
477
478 fstat(fileno(m_logFileHandle), &st);
479 if (st.st_size >= m_maxLogSize)
480 RotateLog(FALSE);
481 }
482
483 MutexUnlock(m_mutexLogAccess);
484 }
485 }
486
487 /**
488 * Format message (UNIX version)
489 */
490 #ifndef _WIN32
491
492 static TCHAR *FormatMessageUX(UINT32 dwMsgId, TCHAR **ppStrings)
493 {
494 const TCHAR *p;
495 TCHAR *pMsg;
496 int i, iSize, iLen;
497
498 if (dwMsgId >= m_numMessages)
499 {
500 // No message with this ID
501 pMsg = (TCHAR *)malloc(64 * sizeof(TCHAR));
502 _sntprintf(pMsg, 64, _T("MSG 0x%08X - Unable to find message text\n"), (unsigned int)dwMsgId);
503 }
504 else
505 {
506 iSize = (_tcslen(m_messages[dwMsgId]) + 2) * sizeof(TCHAR);
507 pMsg = (TCHAR *)malloc(iSize);
508
509 for(i = 0, p = m_messages[dwMsgId]; *p != 0; p++)
510 if (*p == _T('%'))
511 {
512 p++;
513 if ((*p >= _T('1')) && (*p <= _T('9')))
514 {
515 iLen = _tcslen(ppStrings[*p - _T('1')]);
516 iSize += iLen * sizeof(TCHAR);
517 pMsg = (TCHAR *)realloc(pMsg, iSize);
518 _tcscpy(&pMsg[i], ppStrings[*p - _T('1')]);
519 i += iLen;
520 }
521 else
522 {
523 if (*p == 0) // Handle single % character at the string end
524 break;
525 pMsg[i++] = *p;
526 }
527 }
528 else
529 {
530 pMsg[i++] = *p;
531 }
532 pMsg[i++] = _T('\n');
533 pMsg[i] = 0;
534 }
535
536 return pMsg;
537 }
538
539 #endif /* ! _WIN32 */
540
541 /**
542 * Write log record
543 * Parameters:
544 * msg - Message ID
545 * wType - Message type (see ReportEvent() for details)
546 * format - Parameter format string, each parameter represented by one character.
547 * The following format characters can be used:
548 * s - String (multibyte or UNICODE, depending on build)
549 * m - multibyte string
550 * u - UNICODE string
551 * d - Decimal integer
552 * x - Hex integer
553 * e - System error code (will appear in log as textual description)
554 * a - IP address in network byte order
555 * A - IPv6 address in network byte order
556 * H - IPv6 address in network byte order (will appear in [])
557 * I - pointer to InetAddress object
558 */
559 void LIBNETXMS_EXPORTABLE nxlog_write(DWORD msg, WORD wType, const char *format, ...)
560 {
561 va_list args;
562 TCHAR *strings[16], *pMsg, szBuffer[256];
563 int numStrings = 0;
564 UINT32 error;
565 #if defined(UNICODE) && !defined(_WIN32)
566 char *mbMsg;
567 #endif
568
569 if (!(m_flags & NXLOG_IS_OPEN) || (msg == 0))
570 return;
571
572 memset(strings, 0, sizeof(TCHAR *) * 16);
573
574 if (format != NULL)
575 {
576 va_start(args, format);
577
578 for(; (format[numStrings] != 0) && (numStrings < 16); numStrings++)
579 {
580 switch(format[numStrings])
581 {
582 case 's':
583 strings[numStrings] = _tcsdup(va_arg(args, TCHAR *));
584 break;
585 case 'm':
586 #ifdef UNICODE
587 strings[numStrings] = WideStringFromMBString(va_arg(args, char *));
588 #else
589 strings[numStrings] = strdup(va_arg(args, char *));
590 #endif
591 break;
592 case 'u':
593 #ifdef UNICODE
594 strings[numStrings] = wcsdup(va_arg(args, WCHAR *));
595 #else
596 strings[numStrings] = MBStringFromWideString(va_arg(args, WCHAR *));
597 #endif
598 break;
599 case 'd':
600 strings[numStrings] = (TCHAR *)malloc(16 * sizeof(TCHAR));
601 _sntprintf(strings[numStrings], 16, _T("%d"), (int)(va_arg(args, LONG)));
602 break;
603 case 'x':
604 strings[numStrings] = (TCHAR *)malloc(16 * sizeof(TCHAR));
605 _sntprintf(strings[numStrings], 16, _T("0x%08X"), (unsigned int)(va_arg(args, UINT32)));
606 break;
607 case 'a':
608 strings[numStrings] = (TCHAR *)malloc(20 * sizeof(TCHAR));
609 IpToStr(va_arg(args, UINT32), strings[numStrings]);
610 break;
611 case 'A':
612 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
613 Ip6ToStr(va_arg(args, BYTE *), strings[numStrings]);
614 break;
615 case 'H':
616 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
617 strings[numStrings][0] = _T('[');
618 Ip6ToStr(va_arg(args, BYTE *), strings[numStrings] + 1);
619 _tcscat(strings[numStrings], _T("]"));
620 break;
621 case 'I':
622 strings[numStrings] = (TCHAR *)malloc(48 * sizeof(TCHAR));
623 va_arg(args, InetAddress *)->toString(strings[numStrings]);
624 break;
625 case 'e':
626 error = va_arg(args, UINT32);
627 #ifdef _WIN32
628 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
629 FORMAT_MESSAGE_FROM_SYSTEM |
630 FORMAT_MESSAGE_IGNORE_INSERTS,
631 NULL, error,
632 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), // Default language
633 (LPTSTR)&pMsg, 0, NULL) > 0)
634 {
635 pMsg[_tcscspn(pMsg, _T("\r\n"))] = 0;
636 strings[numStrings] = (TCHAR *)malloc((_tcslen(pMsg) + 1) * sizeof(TCHAR));
637 _tcscpy(strings[numStrings], pMsg);
638 LocalFree(pMsg);
639 }
640 else
641 {
642 strings[numStrings] = (TCHAR *)malloc(64 * sizeof(TCHAR));
643 _sntprintf(strings[numStrings], 64, _T("MSG 0x%08X - Unable to find message text"), error);
644 }
645 #else /* _WIN32 */
646 #if HAVE_STRERROR_R
647 #if HAVE_POSIX_STRERROR_R
648 strings[numStrings] = (TCHAR *)malloc(256 * sizeof(TCHAR));
649 _tcserror_r((int)error, strings[numStrings], 256);
650 #else
651 strings[numStrings] = _tcsdup(_tcserror_r((int)error, szBuffer, 256));
652 #endif
653 #else
654 strings[numStrings] = _tcsdup(_tcserror((int)error));
655 #endif
656 #endif
657 break;
658 default:
659 strings[numStrings] = (TCHAR *)malloc(32 * sizeof(TCHAR));
660 _sntprintf(strings[numStrings], 32, _T("BAD FORMAT (0x%08X)"), (unsigned int)(va_arg(args, UINT32)));
661 break;
662 }
663 }
664 va_end(args);
665 }
666
667 #ifdef _WIN32
668 if (!(m_flags & NXLOG_USE_SYSLOG) || (m_flags & NXLOG_PRINT_TO_STDOUT))
669 {
670 LPVOID lpMsgBuf;
671
672 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
673 FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
674 m_msgModuleHandle, msg, 0, (LPTSTR)&lpMsgBuf, 0, (va_list *)strings) > 0)
675 {
676 TCHAR *pCR;
677
678 // Replace trailing CR/LF pair with LF
679 pCR = _tcschr((TCHAR *)lpMsgBuf, _T('\r'));
680 if (pCR != NULL)
681 {
682 *pCR = _T('\n');
683 pCR++;
684 *pCR = 0;
685 }
686 if (m_flags & NXLOG_USE_SYSLOG)
687 {
688 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), (TCHAR *)lpMsgBuf);
689 }
690 else
691 {
692 WriteLogToFile((TCHAR *)lpMsgBuf, wType);
693 }
694 LocalFree(lpMsgBuf);
695 }
696 else
697 {
698 TCHAR message[64];
699
700 _sntprintf(message, 64, _T("MSG 0x%08X - cannot format message"), msg);
701 if (m_flags & NXLOG_USE_SYSLOG)
702 {
703 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), message);
704 }
705 else
706 {
707 WriteLogToFile(message, wType);
708 }
709 }
710 }
711
712 if (m_flags & NXLOG_USE_SYSLOG)
713 {
714 ReportEvent(m_eventLogHandle, (wType == EVENTLOG_DEBUG_TYPE) ? EVENTLOG_INFORMATION_TYPE : wType, 0, msg, NULL, numStrings, 0, (const TCHAR **)strings, NULL);
715 }
716 #else /* _WIN32 */
717 pMsg = FormatMessageUX(msg, strings);
718 if (m_flags & NXLOG_USE_SYSLOG)
719 {
720 int level;
721
722 switch(wType)
723 {
724 case EVENTLOG_ERROR_TYPE:
725 level = LOG_ERR;
726 break;
727 case EVENTLOG_WARNING_TYPE:
728 level = LOG_WARNING;
729 break;
730 case EVENTLOG_INFORMATION_TYPE:
731 level = LOG_NOTICE;
732 break;
733 case EVENTLOG_DEBUG_TYPE:
734 level = LOG_DEBUG;
735 break;
736 default:
737 level = LOG_INFO;
738 break;
739 }
740 #ifdef UNICODE
741 mbMsg = MBStringFromWideString(pMsg);
742 syslog(level, "%s", mbMsg);
743 free(mbMsg);
744 #else
745 syslog(level, "%s", pMsg);
746 #endif
747
748 if (m_flags & NXLOG_PRINT_TO_STDOUT)
749 {
750 m_consoleWriter(_T("%s %s"), FormatLogTimestamp(szBuffer), pMsg);
751 }
752 }
753 else
754 {
755 WriteLogToFile(pMsg, wType);
756 }
757 free(pMsg);
758 #endif /* _WIN32 */
759
760 while(--numStrings >= 0)
761 free(strings[numStrings]);
762 }
763
764 /**
765 * Write debug message
766 */
767 void LIBNETXMS_EXPORTABLE nxlog_debug(int level, const TCHAR *format, ...)
768 {
769 if (level > s_debugLevel)
770 return;
771
772 va_list args;
773 va_start(args, format);
774 TCHAR buffer[8192];
775 _vsntprintf(buffer, 8192, format, args);
776 va_end(args);
777 nxlog_write(s_debugMsg, NXLOG_DEBUG, "s", buffer);
778 }
779
780 /**
781 * Write debug message
782 */
783 void LIBNETXMS_EXPORTABLE nxlog_debug2(int level, const TCHAR *format, va_list args)
784 {
785 if (level > s_debugLevel)
786 return;
787
788 TCHAR buffer[8192];
789 _vsntprintf(buffer, 8192, format, args);
790 nxlog_write(s_debugMsg, NXLOG_DEBUG, "s", buffer);
791 }