Click here to Skip to main content
15,867,704 members
Articles / General Programming
Tip/Trick

Multithreaded, Customizable SysLog Server - C#

Rate me:
Please Sign up or sign in to vote.
3.67/5 (6 votes)
27 Mar 2013CPOL2 min read 60.2K   21   7
Multithreaded, customizable SysLog server in C#.

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:

C#
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;
        }
    }
}

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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Jeff Block12-Jun-15 14:34
Jeff Block12-Jun-15 14:34 
GeneralMy vote of 2 Pin
Michael D Bray18-Aug-14 12:36
Michael D Bray18-Aug-14 12:36 
QuestionThis is not a good way to do multi-threaded code Pin
Michael D Bray18-Aug-14 12:24
Michael D Bray18-Aug-14 12:24 
QuestionReturn this in a Console window Pin
User 951229714-Jul-14 19:33
User 951229714-Jul-14 19:33 
General! Pin
cavalin xucro16-Aug-13 9:33
cavalin xucro16-Aug-13 9:33 
GeneralMy vote of 5 Pin
metastruct20-Aug-12 8:52
metastruct20-Aug-12 8:52 
GeneralRe: My vote of 5 Pin
zulu27-Sep-21 7:07
zulu27-Sep-21 7:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.