Build a Windows Event Log Watcher Service Process to Export Event Log Entries as an RSS Feed

Introduction

The article presented below shows how to develop and set up a process to monitor for Windows Event Log (Application, System, and so forth) changes and export them as RSS feeds. Previously, I wrote an article on how to setup a process to monitor Event Log Changes via/with TCP Listener: Remote Event Log Montior/Watcher (Using TCP in .NET) or Use Windows Management Instrumentation (WMI): Win32_NTLogEvent and ManagementEventWatcher—to Build Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed (see other Siccolo articles about working with .NET, Windows Event Log, C#, and VB.NET).

There are some other ideas on how to "export" Event Log entries into RSS Feeds using ASP.NET; for example, Event Log RSS Feed Generator, or Event Log Monitoring with RSS. In this application, however, I'm using a Windows service to monitor the Windows Event Log for an events associated with a certain Event Source.

The idea is very similar to Remote Event Log Montior/Watcher (Using TCP in .NET); it has a Windows service on a machine monitoring for a certain Event Log entries and exporting them into RSS feed file. After that, any Feed Reader & RSS Aggregator supporting UNC file names can display those Event Log entries as RSS feeds. For example, I'm using intraVnews Feed Reader & RSS Aggregator for Outlook.

As you may know, .NET allows a developer to attach a "handler" to monitor for event log changes (Vb.NET):

         ...
Dim objLog As EventLog = New EventLog("Application")
AddHandler objLog.EntryWritten, _
   AddressOf ApplicationLog_OnEntryWritten
objLog.EnableRaisingEvents = True
         ...

Public Sub ApplicationLog_OnEntryWritten(ByVal [source] As Object, _
   ByVal e As EntryWrittenEventArgs)
   Try

      'handle event log change here

   Catch err As Exception
      'oops
   End Try
End Sub

or, in C#:

EventLog eLog = new EventLog("Application");
eLog.EntryWritten += new
   EntryWrittenEventHandler(EventLog_OnEntryWritten);
eLog.EnableRaisingEvents = true;
...
public void EventLog_OnEntryWritten(object source,
   EntryWrittenEventArgs e)
{
   try
   {
      //handle event log change here
   }
   catch (Exception ex)
   {
      //oops
   }
}

The only problem with this approach is that it does not allow you to monitor for event log changes on a remote machine. See Microsoft support article 815314.

1. Creating an Event Log Watcher Service

First, build a service component—Windows Service application—responsible for "keeping an eye on" event log on a machine. To create service application (in other words, an application that runs as a service):

  • Application that runs as a service, has few events (inherited from System.ServiceProcess.ServiceBase).
  • OnStart(): Occurs when the service receives a Start command.
  • OnStop(): Occurs when the service receives a Stop command.
  • OnPause() and OnContinue(): Occurs when the service receives a Pause/Resume command.

For an Event Log Watcher service, you only need the OnStart() and OnStop() events. The onStart() procedure tells what actions should be taken when Event Log Watcher service starts running; load configuration settings (such as where to output RSS feeds, which Event Logs to monitor, if you need to filter certain event Sources and filter certain Event Types; for example, you may need only to "watch out" for Error event types from MS SQL Server service), and after that actually start working and monitor for Event Log changes!

For example, the config file may look like this:

Where configuration settings are loaded using the ConfigurationManager class.

To load/read settings from the configuration file, I'm using the following simple class LogWatcherSettings (showing just one method for just one configuration setting):

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;

namespace LogWatcher_RSS
{
   class LogWatcherSettings
   {
      ...
      ...
      public static string RssFeedsOutputFoler()
      {
         try
         {
            Configuration config = ConfigurationManager.
               OpenExeConfiguration(ConfigurationUserLevel.None);
            return ConfigurationManager.
               AppSettings["RssOutputFolder"].ToString();
         }
         catch
         {
                return "c:\\";    // by default, log file
         }
      }
      ...
      ...


   }
}

Now, back to the Log Watcher main class log_watch_rss:

public partial class log_watch_rss : ServiceBase
{
   bool m_ToDebug = false;
   string m_DebugFileName = "";

   string m_RSSFeedsFolder = "";    //where to put RSS feed files

   //keep a list of Event Logs to monitor
   //for example, Application and System
  ArrayList m_EventLogsToMonitor = new ArrayList();

    ...
    ...

   protected override void OnStart(string[] args)
   {
      //load settings:
      m_ToDebug = LogWatcherSettings.ToDebug();
      m_DebugFileName = LogWatcherSettings.DebugFile();
      m_RSSFeedsFolder = LogWatcherSettings.RssFeedsOutputFoler();

      //first - load list of event logs to monitor:
      string[] eventLogList =
         LogWatcherSettings.Filter_LogName().Split(',') ;
      if (m_ToDebug)
      {
         AddDebugMessage("LogWatcher_RSS monitors logs
            {" + LogWatcherSettings.Filter_LogName() + "}");
      }

      //for each Event Log - create instances of :
      for (int i = 0; i < eventLogList.Length; i++)
      {
         string logName = eventLogList[i];
         //one instance of EventLogRSS per log:
         EventLogRSS eventLog = new EventLogRSS(logName,
            m_ToDebug,
            m_DebugFileName,
            m_RSSFeedsFolder,
            LogWatcherSettings.Filter_LogSource(),
            LogWatcherSettings.Filter_LogEvent(),
            LogWatcherSettings.HowManyRecordsPull() );
         m_EventLogsToMonitor.Add(eventLog);
      }
   }

      ...
      ...

   protected override void OnStop()
   {
      foreach (EventLogRSS log in m_EventLogsToMonitor)
      {
         log.CloseLog();
      }
   }
}

So, as you can see, the Log Watcher service main class log_watch_rss is not doing a lot of work; it's merely loading settings and creating instances of EventLogRSS. And, the EventLogRSS class is the one monitoring for Windows Event Log changes and "exporting" them to RSS Feed files.

Build a Windows Event Log Watcher Service Process to Export Event Log Entries as an RSS Feed

2. Building the Event Log Watcher/Monitor

Look at the EventLogRSS class. First, in the class constructor:

class EventLogRSS
{

   //class variables:
   private EventLog m_EventLog = null;    //Event log to monitor

   bool m_ToDebug = false;
   string m_DebugFileName = "";
        
   string m_RSSFeedsFolder = "";
   string m_RSSFeedFileName = "";

   string m_EventLogName = "";            //actual name of Event
                                          //log to monitor

   string m_FilterEventSource="";         //filter by Source name
   ArrayList m_FilterEventSourceList = new ArrayList();

   string m_FilterEventType="";           //filter by event type
   ArrayList m_FilterEventTypeList = new ArrayList();

   int m_RecordsToPull=250;               //how many records before
   int m_CurrentRecordCount = 0;          //creating a new RSS file

   //machine where Event Log Watcher service is running
   string m_LocalIP = System.Net.Dns.GetHostName();

   public EventLogRSS(string logName,
      bool toDebug,
      string debugFileName,
      string rssFeedsPath,
      string filterEventSource,
      string filterEventType,
      int recordsToPull)
   {
      m_EventLogName = logName;
      m_ToDebug = toDebug;
      m_DebugFileName = debugFileName;
      m_RSSFeedsFolder = rssFeedsPath;
      //constract RSS Feed File Name:
      m_RSSFeedFileName =
         Path.Combine(m_RSSFeedsFolder, m_EventLogName +
                      "_eventlog_rss.xml");

      //filters
      m_FilterEventSource = filterEventSource;
      m_FilterEventType = filterEventType;

      if (m_FilterEventSource != String.Empty)
      { m_FilterEventSourceList.
        AddRange(m_FilterEventSource.Split(',')); }

      if (m_FilterEventType != String.Empty)
      { m_FilterEventTypeList.
        AddRange(m_FilterEventType.Split(',')); }


      //how many records in a RSS file, before a new file is created
      m_RecordsToPull = recordsToPull;

      //initialize log...
      m_EventLog = new EventLog(logName);
      m_EventLog.EntryWritten +=
         new EntryWrittenEventHandler(EventLog_OnEntryWritten);
      m_EventLog.EnableRaisingEvents = true;

      //create new RSS file
      StartRSSFeed();
      }
   }

So, if and when a new entry is added to Windows Event Log:

m_EventLog.EntryWritten +=
   new EntryWrittenEventHandler(EventLog_OnEntryWritten);
m_EventLog.EnableRaisingEvents = true;

The application will call the EventLog_OnEntryWritten() procedure:

public void EventLog_OnEntryWritten(object source,
   EntryWrittenEventArgs e)
   {
      try
      {
         if (m_ToDebug)
         {
            //or use m_EventLogName
            string eventLogName = ((EventLog)source).LogDisplayName;
            AddDebugMessage(eventLogName + " - " + e.Entry.Source +
                            ":" + e.Entry.Message);
         }
         //filter it?
         if ((m_FilterEventSource == String.Empty ||
              m_FilterEventSourceList.Contains(e.Entry.Source)) &&
              m_FilterEventTypeList.Contains(e.Entry.EntryType.
                 ToString()))
         {
            if (m_CurrentRecordCount > m_RecordsToPull)
            {
               StartRSSFeed();
            }
            //create entry in RSS file...
            AddEntryToRSSFeed((EventLogEntry)e.Entry);
            m_CurrentRecordCount += 1;
         }
         else
         {
            if (m_ToDebug)
            { AddDebugMessage("not in filter --> " +
              e.Entry.Source + ":" + e.Entry.EntryType.ToString());}
         }
      }
      catch (Exception ex_on_entry_written)
      {
         //oh-ho...
         AddDebugMessage("Failed to EventLog_OnEntryWritten() for
                         [" + m_EventLog + "] event log\n" +
                         ex_on_entry_written.Message);
      }
   }

And, if Event Log Entry is for a given Event Source and of specified Event Type, the call to AddEntryToRSSFeed() procedure follows:

public void AddEntryToRSSFeed(EventLogEntry entry)
   {
      try
      {
         XmlDocument rssFeedXMLDoc = new XmlDocument();
         rssFeedXMLDoc.Load(m_RSSFeedFileName);
         XmlElement rssFeedItemElement =
            rssFeedXMLDoc.CreateElement("item");
         //
         rssFeedItemElement.InnerXml =
            "<title></title><link></link><description></description>
             <pubDate></pubDate>";
         rssFeedItemElement["title"].InnerText = entry.Source + "-"
            + entry.EntryType.ToString();
         rssFeedItemElement["link"].InnerText = "";
         rssFeedItemElement["description"].InnerText = entry.Message;
         rssFeedItemElement["pubDate"].InnerText =
            entry.TimeGenerated.ToString("r");

         rssFeedXMLDoc.DocumentElement.SelectNodes
            ("/rss/channel")[0].AppendChild(rssFeedItemElement);
         rssFeedXMLDoc.Save(m_RSSFeedFileName); 
      }

      catch (Exception ex_add_entry_to_rss_fee)
      {
         AddDebugMessage("Failed to AddEntryToRSSFeed() for
            [" + m_EventLog + "] event log\n" +
            ex_add_entry_to_rss_fee.Message);
      }
   }

And that's it. You just developed your own Windows Event Log watcher/monitoring tool. And you just saved, eh-h, about 500 US dollars compared to MOM .

History

No improvements so far. Nearly perfect.



About the Author

Al Siks

check out the free SQL Server Management Tool for mobile devices at Siccolo

Comments

  • There are no comments yet. Be the first to comment!

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Organizations need a comprehensive patch management solution which provides endpoint protection beyond the typical operating system in order to effectively and efficiently minimize risks caused by the vulnerabilities that are inevitable in modern computing systems.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds