65.9K
CodeProject 正在变化。 阅读更多。
Home

多线程、可定制的 SysLog 服务器 - C#

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (6投票s)

2012 年 8 月 16 日

CPOL

2分钟阅读

viewsIcon

62880

C# 中多线程、可定制的 SysLog 服务器。

引言

对于我们这些管理多个设备的人来说,随时了解问题和事件可能是一个挑战。许多 Linux、Unix 和 Windows 设备都支持将 SysLog(系统日志)事件发送到中央服务器以进行通知和/或日志记录。我决定使用 C#(Visual Studio Express 2010)制作此应用程序来接收这些消息,以 CSV 格式存储它们,并根据我定义的自定义条件向我发送电子邮件通知。我选择 CSV 是因为它重量轻,可以由任何电子表格软件原生打开,或者通过简单的 OLEDB 连接导入到另一个应用程序中作为 DataTable。出于我的目的,由于配置为将 SysLog 发送到服务器的设备数量,多线程处理此应用程序至关重要。我让这个应用程序运行了一段时间,并查看了输出 CSV 文件,以确定应该通过电子邮件通知我哪些事件,并将这些事件设置为电子邮件触发器。这是一个相对轻量级的控制台应用程序,用途非常广泛。查看代码并在评论中留下任何问题或建议!您也可以在我的博客上查看 http://meta-struct.com/

使用代码

配置您的“设备”,使其 SysLog 事件指向您计算机的 IP(您可能需要一个静态或保留 IP 才能使其正常工作)。在 Visual Studio 中创建一个控制台应用程序,并使用以下代码

using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace syslog
{
    class Program
    {
        static void Main(string[] args)
        {
            IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
            UdpClient udpListener = new UdpClient(514);
            byte[] bReceive; string sReceive; string sourceIP;

            /* Main Loop */
            /* Listen for incoming data on udp port 514 (default for SysLog events) */
            while (true)
            {
                try
                {
                    bReceive = udpListener.Receive(ref anyIP);
                    /* Convert incoming data from bytes to ASCII */
                    sReceive = Encoding.ASCII.GetString(bReceive);
                    /* Get the IP of the device sending the syslog */
                    sourceIP = anyIP.Address.ToString();
                    new Thread(new logHandler(sourceIP, sReceive).handleLog).Start();
                    /* Start a new thread to handle received syslog event */
                }
                catch (Exception ex) { Console.WriteLine(ex.ToString()); }
            }
        }
    }

    class logHandler
    {
        /* Phrases within the syslog that will trigger an email notification */
        private string[] emailTriggers = new string[] { "link loss", "help please" };
        private string outputPath = @"C:\Users\metastruct\Desktop\syslog.csv"; /* Location to store events */
        private string source; private string log;
        
        public logHandler(string sourceIP, string logData) /* Initialize object and clean up the raw data */
        {
            source = sourceIP.Trim(); /* Client IP */
            log = logData.Replace(Environment.NewLine, "").Trim(); /* Syslog data */
        }

        public void handleLog() /* Store the syslog and determine whether to trigger an email notification */
        {
            /* Store the syslog using a new thread */
            new Thread(new outputCsvRow(outputPath, new string[] { source, log }).addRow).Start();
            for (int i = 0; i < emailTriggers.Count(); i++) { if (log.Contains(emailTriggers[i])) { emailEvent(); } }
            /* Search for trigger strings and send email if found */

            return;
        }

        private void emailEvent() /* Send email notification */
        {
            try
            {
                MailMessage notificationEmail = new MailMessage();
                notificationEmail.Subject = "SysLog Event";
                notificationEmail.IsBodyHtml = true;
                notificationEmail.Body = "<b>SysLog Event Triggered:<br/><br/>Time: </b><br/>" + 
                    DateTime.Now.ToString() + "<br/><b>Source IP: </b><br/>” + 
                    source + “<br/><b>Event: </b><br/>" + log; /* Throw in some basic HTML for readability */
                notificationEmail.From = new MailAddress("SysLog@metastruct.com", "SysLog Server"); /* From Address */
                notificationEmail.To.Add(new MailAddress("metastructblog@gmail.com", "metastruct")); /* To Address */
                SmtpClient emailClient = new SmtpClient("10.10.10.10"); /* Address of your SMTP server of choice */
                //emailClient.UseDefaultCredentials = false; /* If your SMTP server requires credentials to send email */
                //emailClient.Credentials = new NetworkCredential(“username”, “password”); /* Supply User Name and Password */
                emailClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                emailClient.Send(notificationEmail); /* Send the email */
            }
            catch (Exception ex) { Console.WriteLine(ex.ToString()); }
            return;
        }
    }

    class outputCsvRow
    {
        private string formattedRow = null;
        private string outputPath = null;

        public outputCsvRow(string filePath, string[] columns) /* Initialize object */
        {
            outputPath = filePath;
            formattedRow = (char)34 + DateTime.Now.ToString() + (char)34; /* Construct csv row starting with the timestamp */
            for (int i = 0; i < columns.Count(); i++) { formattedRow += "," + (char)34 + columns[i] + (char)34; }
        }

        public void addRow()
        {
            int attempts = 0;
            bool canAccess = false;
            StreamWriter logWriter = null;
            if (!File.Exists(outputPath)) /* If the file doesn't exist, give it some column headers */
            {
                logWriter = new StreamWriter(outputPath, true);
                logWriter.WriteLine((char)34 + "Event_Time" + (char)34 + "," + 
                  (char)34 + "Device_IP" + (char)34 + "," + (char)34 + "SysLog" + (char)34);
                logWriter.Close();
            }
            /* Thread safety first! This is a poor man's SpinLock */
            while (true)
            {
                try
                {
                    logWriter = new StreamWriter(outputPath, true); /* Try to open the file for writing */
                    canAccess = true; /* Success! */
                    break;
                }
                catch (IOException ex)
                {
                    if (attempts < 15) { attempts++; Thread.Sleep(50); }
                    else { Console.WriteLine(ex.ToString()); break; } /* Give up after 15 attempts */
                }
            }
            if (canAccess) /* Write the line if the file is accessible */
            {
                logWriter.WriteLine(formattedRow);
                logWriter.Close();
            }
            return;
        }
    }
}

关注点

另一个可能证明有用的功能是将 IP 地址与更“用户友好”的设备名称进行交叉引用。如果电子邮件或日志被多个用户使用,或者设备数量很多,并且记住所有设备的 IP 地址不太可能,这将非常有用。对于我的“实时”应用程序,我还使用该功能并为每个设备分隔日志文件。警告,有些设备会发送大量这些事件,如果您让它运行,您的轻量级 CSV 文件可能会变得非常大。我还在 Main 块中添加了一些代码,以滚动七天为基础存档日志。为此,您可能必须暂时停止接收日志以避免 IOException

历史 

  • 这是第 1 版,请帮助我改进!
© . All rights reserved.