Introduction
For those of us managing multiple devices, keeping aware of issues and events can be a challenge. Many Linux, Unix, and Windows devices
support the ability to send SysLog (System Log) events to a central server for notifications and/or logging. I decided to make this
application using C# (Visual Studio Express 2010) to receive those messages, store them in CSV format and send me email notifications based
on custom criteria I define. I chose CSV because it's light weight and can natively be opened by any spreadsheet software or brought in to another app
as a DataTable with a simple OLEDB connection. For my purposes, multithreading this app was essential due to the volume of devices configured to send SysLogs to
the server. I let this app run for a bit and looked through the output CSV file to determine which events I should be made aware of via email,
and set those as email triggers. This is a relatively light weight console app that is very versatile for a number of uses. Check out the
code and leave a comment with any questions or suggestions! You can also check out my blog at http://meta-struct.com/.
Using the code
Configure your "devices" to aim their SysLog events to the IP of your computer (you'll probably want a static or reserved IP for this to work well).
Create a Console application in Visual Studio, and use the following code:
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;
while (true)
{
try
{
bReceive = udpListener.Receive(ref anyIP);
sReceive = Encoding.ASCII.GetString(bReceive);
sourceIP = anyIP.Address.ToString();
new Thread(new logHandler(sourceIP, sReceive).handleLog).Start();
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
}
}
}
class logHandler
{
private string[] emailTriggers = new string[] { "link loss", "help please" };
private string outputPath = @"C:\Users\metastruct\Desktop\syslog.csv";
private string source; private string log;
public logHandler(string sourceIP, string logData)
{
source = sourceIP.Trim();
log = logData.Replace(Environment.NewLine, "").Trim();
}
public void handleLog()
{
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(); } }
return;
}
private void emailEvent()
{
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;
notificationEmail.From = new MailAddress("SysLog@metastruct.com", "SysLog Server");
notificationEmail.To.Add(new MailAddress("metastructblog@gmail.com", "metastruct"));
SmtpClient emailClient = new SmtpClient("10.10.10.10");
emailClient.DeliveryMethod = SmtpDeliveryMethod.Network;
emailClient.Send(notificationEmail);
}
catch (Exception ex) { Console.WriteLine(ex.ToString()); }
return;
}
}
class outputCsvRow
{
private string formattedRow = null;
private string outputPath = null;
public outputCsvRow(string filePath, string[] columns)
{
outputPath = filePath;
formattedRow = (char)34 + DateTime.Now.ToString() + (char)34;
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))
{
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();
}
while (true)
{
try
{
logWriter = new StreamWriter(outputPath, true);
canAccess = true;
break;
}
catch (IOException ex)
{
if (attempts < 15) { attempts++; Thread.Sleep(50); }
else { Console.WriteLine(ex.ToString()); break; }
}
}
if (canAccess)
{
logWriter.WriteLine(formattedRow);
logWriter.Close();
}
return;
}
}
}
Points of Interest
An additional feature that may prove useful would be a function to cross reference IP addresses with a more "user friendly" device name.
This would be most useful if the emails or logs are being used by multiple users, or if there are a large amount of devices and remembering the IP addresses
of all of them isn't probable. For my "live" application, I also use that feature and separate log files for each device. Word of warning,
some devices send tons of these events and your light weight CSV file can become enormous if you let it. I also added some code in the
Main
block
to archive the logs on a rolling seven day basis. For that you'll probably have to halt receiving logs briefly to avoid an
IOException
.
History
- This is version 1, help me improve!