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