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