syslog message write to database optimized
[public/netxms.git] / src / server / core / syslogd.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: syslogd.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24 #include <nxlog.h>
25 #include <nxlpapi.h>
26
27 /**
28 * Max syslog message length
29 */
30 #define MAX_SYSLOG_MSG_LEN 1024
31
32 /**
33 * Queued syslog message structure
34 */
35 struct QUEUED_SYSLOG_MESSAGE
36 {
37 UINT32 dwSourceIP;
38 int nBytes;
39 char *psMsg;
40 };
41
42 /**
43 * Static data
44 */
45 static UINT64 s_msgId = 1;
46 static Queue s_syslogProcessingQueue(1000, 100);
47 static Queue s_syslogWriteQueue(1000, 100);
48 static LogParser *s_parser = NULL;
49 static MUTEX s_parserLock = INVALID_MUTEX_HANDLE;
50
51 /**
52 * Parse timestamp field
53 */
54 static BOOL ParseTimeStamp(char **ppStart, int nMsgSize, int *pnPos, time_t *ptmTime)
55 {
56 static char psMonth[12][5] = { "Jan ", "Feb ", "Mar ", "Apr ",
57 "May ", "Jun ", "Jul ", "Aug ",
58 "Sep ", "Oct ", "Nov ", "Dec " };
59 struct tm timestamp;
60 time_t t;
61 char szBuffer[16], *pCurr = *ppStart;
62 int i;
63
64 if (nMsgSize - *pnPos < 16)
65 return FALSE; // Timestamp cannot be shorter than 16 bytes
66
67 // Prepare local time structure
68 t = time(NULL);
69 memcpy(&timestamp, localtime(&t), sizeof(struct tm));
70
71 // Month
72 for(i = 0; i < 12; i++)
73 if (!memcmp(pCurr, psMonth[i], 4))
74 {
75 timestamp.tm_mon = i;
76 break;
77 }
78 if (i == 12)
79 return FALSE;
80 pCurr += 4;
81
82 // Day of week
83 if (isdigit(*pCurr))
84 {
85 timestamp.tm_mday = *pCurr - '0';
86 }
87 else
88 {
89 if (*pCurr != ' ')
90 return FALSE; // Invalid day of month
91 timestamp.tm_mday = 0;
92 }
93 pCurr++;
94 if (isdigit(*pCurr))
95 {
96 timestamp.tm_mday = timestamp.tm_mday * 10 + (*pCurr - '0');
97 }
98 else
99 {
100 return FALSE; // Invalid day of month
101 }
102 pCurr++;
103 if (*pCurr != ' ')
104 return FALSE;
105 pCurr++;
106
107 // HH:MM:SS
108 memcpy(szBuffer, pCurr, 8);
109 szBuffer[8] = 0;
110 if (sscanf(szBuffer, "%02d:%02d:%02d", &timestamp.tm_hour,
111 &timestamp.tm_min, &timestamp.tm_sec) != 3)
112 return FALSE; // Invalid time format
113 pCurr += 8;
114
115 // Check for Cisco variant - HH:MM:SS.nnn
116 if (*pCurr == '.')
117 {
118 pCurr++;
119 if (isdigit(*pCurr))
120 pCurr++;
121 if (isdigit(*pCurr))
122 pCurr++;
123 if (isdigit(*pCurr))
124 pCurr++;
125 }
126
127 if (*pCurr != ' ')
128 return FALSE; // Space should follow timestamp
129 pCurr++;
130
131 // Convert to system time
132 *ptmTime = mktime(&timestamp);
133 if (*ptmTime == ((time_t)-1))
134 return FALSE;
135
136 // Adjust current position
137 *pnPos += (int)(pCurr - *ppStart);
138 *ppStart = pCurr;
139 return TRUE;
140 }
141
142 /**
143 * Parse syslog message
144 */
145 static BOOL ParseSyslogMessage(char *psMsg, int nMsgLen, NX_SYSLOG_RECORD *pRec)
146 {
147 int i, nLen, nPos = 0;
148 char *pCurr = psMsg;
149
150 memset(pRec, 0, sizeof(NX_SYSLOG_RECORD));
151
152 // Parse PRI part
153 if (*psMsg == '<')
154 {
155 int nPri = 0, nCount = 0;
156
157 for(pCurr++, nPos++; isdigit(*pCurr) && (nPos < nMsgLen); pCurr++, nPos++, nCount++)
158 nPri = nPri * 10 + (*pCurr - '0');
159 if (nPos >= nMsgLen)
160 return FALSE; // Unexpected end of message
161
162 if ((*pCurr == '>') && (nCount > 0) && (nCount <4))
163 {
164 pRec->nFacility = nPri / 8;
165 pRec->nSeverity = nPri % 8;
166 pCurr++;
167 nPos++;
168 }
169 else
170 {
171 return FALSE; // Invalid message
172 }
173 }
174 else
175 {
176 // Set default PRI of 13
177 pRec->nFacility = 1;
178 pRec->nSeverity = SYSLOG_SEVERITY_NOTICE;
179 }
180
181 // Parse HEADER part
182 if (ParseTimeStamp(&pCurr, nMsgLen, &nPos, &pRec->tmTimeStamp))
183 {
184 // Hostname
185 for(i = 0; (*pCurr >= 33) && (*pCurr <= 126) && (i < MAX_SYSLOG_HOSTNAME_LEN - 1) && (nPos < nMsgLen); i++, nPos++, pCurr++)
186 pRec->szHostName[i] = *pCurr;
187 if ((nPos >= nMsgLen) || (*pCurr != ' '))
188 {
189 // Not a valid hostname, assuming to be a part of message
190 pCurr -= i;
191 nPos -= i;
192 pRec->szHostName[0] = 0;
193 }
194 else
195 {
196 pCurr++;
197 nPos++;
198 }
199 }
200 else
201 {
202 pRec->tmTimeStamp = time(NULL);
203 }
204
205 // Parse MSG part
206 for(i = 0; isalnum(*pCurr) && (i < MAX_SYSLOG_TAG_LEN) && (nPos < nMsgLen); i++, nPos++, pCurr++)
207 pRec->szTag[i] = *pCurr;
208 if ((i == MAX_SYSLOG_TAG_LEN) || (nPos >= nMsgLen))
209 {
210 // Too long tag, assuming that it's a part of message
211 pRec->szTag[0] = 0;
212 }
213 pCurr -= i;
214 nPos -= i;
215 nLen = min(nMsgLen - nPos, MAX_LOG_MSG_LENGTH);
216 memcpy(pRec->szMessage, pCurr, nLen);
217
218 return TRUE;
219 }
220
221 /**
222 * Bind syslog message to NetXMS node object
223 * dwSourceIP is an IP address from which we receive message
224 */
225 static void BindMsgToNode(NX_SYSLOG_RECORD *pRec, UINT32 dwSourceIP)
226 {
227 Node *pNode = NULL;
228 UINT32 dwIpAddr;
229
230 // Determine IP address of a source
231 if (pRec->szHostName[0] == 0)
232 {
233 // Hostname was not defined in the message
234 dwIpAddr = dwSourceIP;
235 }
236 else
237 {
238 dwIpAddr = ntohl(ResolveHostNameA(pRec->szHostName));
239 if ((dwIpAddr == INADDR_NONE) || (dwIpAddr == INADDR_ANY))
240 {
241 #ifdef UNICODE
242 WCHAR wname[MAX_OBJECT_NAME];
243 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pRec->szHostName, -1, wname, MAX_OBJECT_NAME);
244 wname[MAX_OBJECT_NAME - 1] = 0;
245 pNode = (Node *)FindObjectByName(wname, OBJECT_NODE);
246 #else
247 pNode = (Node *)FindObjectByName(pRec->szHostName, OBJECT_NODE);
248 #endif
249 if (pNode == NULL)
250 dwIpAddr = dwSourceIP;
251 }
252 }
253
254 // Match source IP to NetXMS object
255 if ((dwIpAddr != INADDR_NONE) && (pNode == NULL))
256 pNode = FindNodeByIP(0, dwIpAddr);
257
258 if (pNode != NULL)
259 {
260 pRec->dwSourceObject = pNode->Id();
261 if (pRec->szHostName[0] == 0)
262 {
263 #ifdef UNICODE
264 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pNode->Name(), -1, pRec->szHostName, MAX_SYSLOG_HOSTNAME_LEN, NULL, NULL);
265 pRec->szHostName[MAX_SYSLOG_HOSTNAME_LEN - 1] = 0;
266 #else
267 nx_strncpy(pRec->szHostName, pNode->Name(), MAX_SYSLOG_HOSTNAME_LEN);
268 #endif
269 }
270 }
271 else
272 {
273 if (pRec->szHostName[0] == 0)
274 IpToStrA(dwSourceIP, pRec->szHostName);
275 }
276 }
277
278 /**
279 * Handler for EnumerateSessions()
280 */
281 static void BroadcastSyslogMessage(ClientSession *pSession, void *pArg)
282 {
283 if (pSession->isAuthenticated())
284 pSession->onSyslogMessage((NX_SYSLOG_RECORD *)pArg);
285 }
286
287 /**
288 * Syslog writer thread
289 */
290 static THREAD_RESULT THREAD_CALL SyslogWriterThread(void *arg)
291 {
292 DbgPrintf(1, _T("Syslog writer thread started"));
293 while(true)
294 {
295 NX_SYSLOG_RECORD *r = (NX_SYSLOG_RECORD *)s_syslogWriteQueue.GetOrBlock();
296 if (r == INVALID_POINTER_VALUE)
297 break;
298
299 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
300
301 DB_STATEMENT hStmt = DBPrepare(hdb, _T("INSERT INTO syslog (msg_id,msg_timestamp,facility,severity,source_object_id,hostname,msg_tag,msg_text) VALUES (?,?,?,?,?,?,?,?)"));
302 if (hStmt == NULL)
303 {
304 free(r);
305 continue;
306 }
307
308 int count = 0;
309 DBBegin(hdb);
310 while(true)
311 {
312 DBBind(hStmt, 1, DB_SQLTYPE_BIGINT, r->qwMsgId);
313 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (INT32)r->tmTimeStamp);
314 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, r->nFacility);
315 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, r->nSeverity);
316 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, r->dwSourceObject);
317 #ifdef UNICODE
318 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szHostName), DB_BIND_DYNAMIC);
319 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szTag), DB_BIND_DYNAMIC);
320 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, WideStringFromMBString(r->szMessage), DB_BIND_DYNAMIC);
321 #else
322 DBBind(hStmt, 6, DB_SQLTYPE_VARCHAR, r->szHostName, DB_BIND_STATIC);
323 DBBind(hStmt, 7, DB_SQLTYPE_VARCHAR, r->szTag, DB_BIND_STATIC);
324 DBBind(hStmt, 8, DB_SQLTYPE_VARCHAR, r->szMessage, DB_BIND_STATIC);
325 #endif
326
327 if (!DBExecute(hStmt))
328 {
329 free(r);
330 break;
331 }
332 free(r);
333 count++;
334 if (count == 1000)
335 break;
336 r = (NX_SYSLOG_RECORD *)s_syslogWriteQueue.Get();
337 if ((r == NULL) || (r == INVALID_POINTER_VALUE))
338 break;
339 }
340 DBCommit(hdb);
341 DBFreeStatement(hStmt);
342 DBConnectionPoolReleaseConnection(hdb);
343 if (r == INVALID_POINTER_VALUE)
344 break;
345 }
346 DbgPrintf(1, _T("Syslog writer thread stopped"));
347 return THREAD_OK;
348 }
349
350 /**
351 * Process syslog message
352 */
353 static void ProcessSyslogMessage(char *psMsg, int nMsgLen, UINT32 dwSourceIP)
354 {
355 NX_SYSLOG_RECORD record;
356
357 DbgPrintf(6, _T("ProcessSyslogMessage: Raw syslog message to process:\n%hs"), psMsg);
358 if (ParseSyslogMessage(psMsg, nMsgLen, &record))
359 {
360 record.qwMsgId = s_msgId++;
361 BindMsgToNode(&record, dwSourceIP);
362
363 s_syslogWriteQueue.Put(nx_memdup(&record, sizeof(NX_SYSLOG_RECORD)));
364
365 // Send message to all connected clients
366 EnumerateClientSessions(BroadcastSyslogMessage, &record);
367
368 TCHAR ipAddr[32];
369 DbgPrintf(6, _T("Syslog message: ipAddr=%s objectId=%d tag=\"%hs\" msg=\"%hs\""),
370 IpToStr(dwSourceIP, ipAddr), record.dwSourceObject, record.szTag, record.szMessage);
371
372 MutexLock(s_parserLock);
373 if ((record.dwSourceObject != 0) && (s_parser != NULL))
374 {
375 #ifdef UNICODE
376 WCHAR wtag[MAX_SYSLOG_TAG_LEN];
377 WCHAR wmsg[MAX_LOG_MSG_LENGTH];
378 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, record.szTag, -1, wtag, MAX_SYSLOG_TAG_LEN);
379 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, record.szMessage, -1, wmsg, MAX_LOG_MSG_LENGTH);
380 s_parser->matchEvent(wtag, record.nFacility, 1 << record.nSeverity, wmsg, record.dwSourceObject);
381 #else
382 s_parser->matchEvent(record.szTag, record.nFacility, 1 << record.nSeverity, record.szMessage, record.dwSourceObject);
383 #endif
384 }
385 MutexUnlock(s_parserLock);
386 }
387 else
388 {
389 DbgPrintf(6, _T("ProcessSyslogMessage: Cannot parse syslog message"));
390 }
391 }
392
393 /**
394 * Syslog processing thread
395 */
396 static THREAD_RESULT THREAD_CALL SyslogProcessingThread(void *pArg)
397 {
398 QUEUED_SYSLOG_MESSAGE *pMsg;
399
400 while(1)
401 {
402 pMsg = (QUEUED_SYSLOG_MESSAGE *)s_syslogProcessingQueue.GetOrBlock();
403 if (pMsg == INVALID_POINTER_VALUE)
404 break;
405
406 ProcessSyslogMessage(pMsg->psMsg, pMsg->nBytes, pMsg->dwSourceIP);
407 free(pMsg->psMsg);
408 free(pMsg);
409 }
410 return THREAD_OK;
411 }
412
413 /**
414 * Queue syslog message for processing
415 */
416 static void QueueSyslogMessage(char *psMsg, int nMsgLen, UINT32 dwSourceIP)
417 {
418 QUEUED_SYSLOG_MESSAGE *pMsg;
419
420 pMsg = (QUEUED_SYSLOG_MESSAGE *)malloc(sizeof(QUEUED_SYSLOG_MESSAGE));
421 pMsg->dwSourceIP = dwSourceIP;
422 pMsg->nBytes = nMsgLen;
423 pMsg->psMsg = (char *)nx_memdup(psMsg, nMsgLen + 1);
424 s_syslogProcessingQueue.Put(pMsg);
425 }
426
427 /**
428 * Callback for syslog parser
429 */
430 static void SyslogParserCallback(DWORD eventCode, const TCHAR *eventName, const TCHAR *line, int paramCount,
431 TCHAR **params, DWORD objectId, void *userArg)
432 {
433 char format[] = "ssssssssssssssssssssssssssssssss";
434 TCHAR *plist[32];
435 int i, count;
436
437 count = min(paramCount, 32);
438 format[count] = 0;
439 for(i = 0; i < count; i++)
440 plist[i] = params[i];
441 PostEvent(eventCode, objectId, format,
442 plist[0], plist[1], plist[2], plist[3],
443 plist[4], plist[5], plist[6], plist[7],
444 plist[8], plist[9], plist[10], plist[11],
445 plist[12], plist[13], plist[14], plist[15],
446 plist[16], plist[17], plist[18], plist[19],
447 plist[20], plist[21], plist[22], plist[23],
448 plist[24], plist[25], plist[26], plist[27],
449 plist[28], plist[29], plist[30], plist[31]);
450 }
451
452 /**
453 * Event name resolver
454 */
455 static bool EventNameResolver(const TCHAR *name, DWORD *code)
456 {
457 EVENT_TEMPLATE *event;
458 bool success = false;
459
460 event = FindEventTemplateByName(name);
461 if (event != NULL)
462 {
463 *code = event->dwCode;
464 success = true;
465 }
466 return success;
467 }
468
469 /**
470 * Create syslog parser from config
471 */
472 static void CreateParserFromConfig()
473 {
474 char *xml;
475
476 MutexLock(s_parserLock);
477 delete_and_null(s_parser);
478 #ifdef UNICODE
479 WCHAR *wxml = ConfigReadCLOB(_T("SyslogParser"), _T("<parser></parser>"));
480 if (wxml != NULL)
481 {
482 xml = UTF8StringFromWideString(wxml);
483 free(wxml);
484 }
485 else
486 {
487 xml = NULL;
488 }
489 #else
490 xml = ConfigReadCLOB("SyslogParser", "<parser></parser>");
491 #endif
492 if (xml != NULL)
493 {
494 TCHAR parseError[256];
495 ObjectArray<LogParser> *parsers = LogParser::createFromXml(xml, -1, parseError, 256, EventNameResolver);
496 if ((parsers != NULL) && (parsers->size() > 0))
497 {
498 s_parser = parsers->get(0);
499 s_parser->setCallback(SyslogParserCallback);
500 DbgPrintf(3, _T("syslogd: parser successfully created from config"));
501 }
502 else
503 {
504 nxlog_write(MSG_SYSLOG_PARSER_INIT_FAILED, EVENTLOG_ERROR_TYPE, "s", parseError);
505 }
506 free(xml);
507 delete parsers;
508 }
509 MutexUnlock(s_parserLock);
510 }
511
512 /**
513 * Syslog messages receiver thread
514 */
515 THREAD_RESULT THREAD_CALL SyslogDaemon(void *pArg)
516 {
517 SOCKET hSocket;
518 struct sockaddr_in addr;
519 int nBytes, nPort, nRet;
520 socklen_t nAddrLen;
521 char sMsg[MAX_SYSLOG_MSG_LEN + 1];
522 DB_RESULT hResult;
523 fd_set rdfs;
524 struct timeval tv;
525
526 // Determine first available message id
527 hResult = DBSelect(g_hCoreDB, _T("SELECT max(msg_id) FROM syslog"));
528 if (hResult != NULL)
529 {
530 if (DBGetNumRows(hResult) > 0)
531 {
532 s_msgId = max(DBGetFieldUInt64(hResult, 0, 0) + 1, s_msgId);
533 }
534 DBFreeResult(hResult);
535 }
536
537 hSocket = socket(AF_INET, SOCK_DGRAM, 0);
538 if (hSocket == INVALID_SOCKET)
539 {
540 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("SyslogDaemon"));
541 return THREAD_OK;
542 }
543
544 SetSocketExclusiveAddrUse(hSocket);
545 SetSocketReuseFlag(hSocket);
546
547 // Get listen port number
548 nPort = ConfigReadInt(_T("SyslogListenPort"), 514);
549 if ((nPort < 1) || (nPort > 65535))
550 nPort = 514;
551
552 // Fill in local address structure
553 memset(&addr, 0, sizeof(struct sockaddr_in));
554 addr.sin_family = AF_INET;
555 addr.sin_addr.s_addr = ResolveHostName(g_szListenAddress);
556 addr.sin_port = htons((WORD)nPort);
557
558 // Bind socket
559 if (bind(hSocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
560 {
561 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", nPort, _T("SyslogDaemon"), WSAGetLastError());
562 closesocket(hSocket);
563 return THREAD_OK;
564 }
565 nxlog_write(MSG_LISTENING_FOR_SYSLOG, EVENTLOG_INFORMATION_TYPE, "ad", ntohl(addr.sin_addr.s_addr), nPort);
566
567 // Create message parser
568 s_parserLock = MutexCreate();
569 CreateParserFromConfig();
570
571 // Start processing thread
572 THREAD hProcessingThread = ThreadCreateEx(SyslogProcessingThread, 0, NULL);
573 THREAD hWriterThread = ThreadCreateEx(SyslogWriterThread, 0, NULL);
574
575 DbgPrintf(1, _T("Syslog Daemon started"));
576
577 // Wait for packets
578 while(!IsShutdownInProgress())
579 {
580 FD_ZERO(&rdfs);
581 FD_SET(hSocket, &rdfs);
582 tv.tv_sec = 1;
583 tv.tv_usec = 0;
584 nRet = select((int)hSocket + 1, &rdfs, NULL, NULL, &tv);
585 if (nRet > 0)
586 {
587 nAddrLen = sizeof(struct sockaddr_in);
588 nBytes = recvfrom(hSocket, sMsg, MAX_SYSLOG_MSG_LEN, 0, (struct sockaddr *)&addr, &nAddrLen);
589 if (nBytes > 0)
590 {
591 sMsg[nBytes] = 0;
592 QueueSyslogMessage(sMsg, nBytes, ntohl(addr.sin_addr.s_addr));
593 }
594 else
595 {
596 // Sleep on error
597 ThreadSleepMs(100);
598 }
599 }
600 else if (nRet == -1)
601 {
602 // Sleep on error
603 ThreadSleepMs(100);
604 }
605 }
606
607 // Stop processing thread
608 s_syslogProcessingQueue.Put(INVALID_POINTER_VALUE);
609 ThreadJoin(hProcessingThread);
610
611 // Stop writer thread - it must be done after processing thread already finished
612 s_syslogWriteQueue.Put(INVALID_POINTER_VALUE);
613 ThreadJoin(hWriterThread);
614
615 delete s_parser;
616
617 DbgPrintf(1, _T("Syslog Daemon stopped"));
618 return THREAD_OK;
619 }
620
621 /**
622 * Create NXCP message from NX_SYSLOG_RECORD structure
623 */
624 void CreateMessageFromSyslogMsg(CSCPMessage *pMsg, NX_SYSLOG_RECORD *pRec)
625 {
626 UINT32 dwId = VID_SYSLOG_MSG_BASE;
627
628 pMsg->SetVariable(VID_NUM_RECORDS, (UINT32)1);
629 pMsg->SetVariable(dwId++, pRec->qwMsgId);
630 pMsg->SetVariable(dwId++, (UINT32)pRec->tmTimeStamp);
631 pMsg->SetVariable(dwId++, (WORD)pRec->nFacility);
632 pMsg->SetVariable(dwId++, (WORD)pRec->nSeverity);
633 pMsg->SetVariable(dwId++, pRec->dwSourceObject);
634 pMsg->SetVariableFromMBString(dwId++, pRec->szHostName);
635 pMsg->SetVariableFromMBString(dwId++, pRec->szTag);
636 pMsg->SetVariableFromMBString(dwId++, pRec->szMessage);
637 }
638
639 /**
640 * Reinitialize parser on configuration change
641 */
642 void ReinitializeSyslogParser()
643 {
644 if (s_parserLock == INVALID_MUTEX_HANDLE)
645 return; // Syslog daemon not initialized
646 CreateParserFromConfig();
647 }