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






3.67/5 (6投票s)
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 版,请帮助我改进!