Title:  Build Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed
Author:      Greg Dubinovskiy 
Email:       [email protected]
Environment: .NET, Windows
Keywords:    Build Event Log Watcher Service - Create RSS Feed from Event Log - Log Watcher RSS Feed Service - Manage EventLog .NET (OnEntryWritten - EnableRaisingEvents)
Level:       Intermediate
Description: Build Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed
Section      Miscellaneous
SubSection   General

Siccolo Development Articles - Build Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed
Google

Introduction

The article presented bellow, shows how to develop and setup a process to monitor for Windows Event Log (Application, System etc) 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) (on code project - How to Build Simple 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're 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 Windows service to monitor Windows Event Log for an events associated with a certain Event Source.

So idea is very similar to Remote Event Log Montior/Watcher (Using TCP in .NET) - have a Windows service on a machine monitoring for a certain Event Log entries and export them into RSS feed file. After that, any Feed Reader & RSS Aggregator supporting UNC file names will be able to 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, 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 - it does not allow to monitor for event log changes on a remote machine. See Microsoft support article 815314.


1. Creating Event Log Watcher Service

First, let's build a service component - Windows Service application - responsible for "keep an eye on" event log on a machine. To create service application (i.e. application that runs as a service):





Side Note:
EventLog Class - Provides interaction with Windows event logs and lets you access or customize Windows event logs, which record information about important software or hardware events. Using EventLog, you can read from existing logs, write entries to logs, create or delete event sources, delete logs, and respond to log entries. You can also create new logs when creating an event source.



For Event Log Watcher service, we're only need OnStart() and OnStop() events.
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 need to filter certain event Sources and filter certain Event Types - for example, we may need to only "watch out" for Error event types from MS SQL Server service) and after then actually start working and monitor for Event Log changes!

For example, config file, may look like this:





Where configuration settings are loaded using ConfigurationManager class

Side Note:
ConfigurationManager Class - Provides access to configuration files for client applications and allows you to access machine, application, and user configuration information.

To load/read settings from 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 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

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

        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();
            }
        }
    }


Side Note:
ArrayList Class - Implements the Represents a non-generic collection of objects that can be individually accessed by index interface using an array whose size is dynamically increased as required. The capacity of a ArrayList is the number of elements the ArrayList can hold. The default initial capacity for an ArrayList is 0. As elements are added to a ArrayList, the capacity is automatically increased as required through reallocation. The capacity can be decreased by calling TrimToSize or by setting the Capacity property explicitly.
Elements in this collection can be accessed using an integer index. Indexes in this collection are zero-based.
ArrayList accepts a null reference (Nothing in Visual Basic) as a valid value and allows duplicate elements.

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

2. Building Event Log Watcher/Monitor:

Let's look at EventLogRSS class. First, in the class constractor:
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 creating a new RSS file
        int m_CurrentRecordCount = 0;

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

        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(',')); }


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

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

            //create new RSS file
            StartRSSFeed();
        }
    }
So, when and if new entry is being added to Windows Event Log:
  m_EventLog.EntryWritten += new EntryWrittenEventHandler(EventLog_OnEntryWritten);
  m_EventLog.EnableRaisingEvents = true;


Side Note:
EventLog.EntryWritten Event - Occurs when an entry is written to an event log on the local computer. To get event notifications, you must set EnableRaisingEvents to true. You can only receive event notifications when entries are written on the local computer. You cannot receive notifications for entries written on remote computers.
When you create an EntryWritten delegate, you identify the method that will handle the event. To associate the event with your event handler, add an instance of the delegate to the event. The event handler is called whenever the event occurs, until you remove the delegate. For more information about handling events with delegates, see Consuming Events.
The system responds to WriteEntry only if the last write event occurred at least six seconds previously. This implies you will only receive one EntryWritten event notification within a six-second interval, even if more than one event log change occurs. If you insert a sufficiently long sleep interval (around 10 seconds) between calls to WriteEntry, you are less likely to miss an event. However, if write events occur more frequently, you might not receive the event notification until the next interval. Typically, missed event notifications are not lost, but delayed.

Application will call EventLog_OnEntryWritten() procedure:
public void EventLog_OnEntryWritten(object source, EntryWrittenEventArgs e)
    {
            try
            {
                if (m_ToDebug)
                {
                    string eventLogName = ((EventLog)source).LogDisplayName;    //or use m_EventLogName
                    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);
            }
    }


Side Note:
EntryWrittenEventArgs Members - Provides data for the EntryWritten event.
EntryWrittenEventArgs.Entry Property - Gets the event log entry that was written to the log.

And if, Event Log Entry is for a given Event Source and of specified Event Type, then 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 = "";
                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. We just developed our own Windows Event Log watcher/monitoring tool. And you just saved, eh-h, about 500 US dollars compare to MOM .



History

no improvements so far. nearly perfect.


Article keywords: Windows Event Log, Event Log Watcher Service, RSS, RSS Feeds, Manage EventLog with .NET, Remote Event Log Montior/Watcher, New EventLog, EnableRaisingEvents, AddHandler, EntryWritten, Windows Service application C#, OnStart(), OnStop(), ConfigurationManager, OpenExeConfiguration(), ConfigurationManager.AppSettings, ToString(), XmlDocument(), DocumentElement, SelectNodes(), XmlDocument.Save()

Back To Articles Page

Free Mobile Management For SQL Server(s!) - Siccolo - SQL Management ToolQuestions? Suggestions? Concerns? - email me to [email protected]    Greg Dubinovsky © 2007
or share your thoughts at Siccolo Blog

web sponsor - siccolo.com. well being sponsor - Enabling clinical and operational value across the continuum of care.
Siccolo - SQL Server Management Tool For Mobile Devices is packed with built-in functionality and tools. Siccolo delivers a rich set of management tools for both DBAs and sys admins. SQL Server management has always been an area of DBA concern. The new Management Tool For Mobile Devices - Siccolo - has simple "Enterprise Manager" and the "Query Analyzer". Siccolo is a management tool for the MS SQL Server with administration capabilities and a database query tool. The administration features provide users the ability to browse database structures. An integrated query tool allows users to quickly create, edit and execute SQL queries and scripts. Siccolo also provides an export tool to allow users to easily save and email execution results. Siccolo helps database professionals save time and increase their productivity by utilizing a more efficient approach to database management - use their Windows Mobile empowered device while sipping margarita on the beach For increased security, Siccolo is configured to run under SSL with IIS authentication.