构建 Windows 事件日志监视器服务进程以将事件日志条目导出为 RSS Feed
构建 Windows 事件日志监视器服务进程以将事件日志条目导出为 RSS feed。
引言
本文介绍了如何开发和设置一个进程来监视 Windows 事件日志(应用程序、系统等)的更改,并将它们导出为 RSS 源。
之前,我写了一篇文章关于如何设置一个进程,通过/使用 TCP 监听器来监视事件日志更改 - 远程事件日志监视器/观察器(在 .NET 中使用 TCP) (在 Code Project 上 - 如何构建简单的事件日志监视器/观察器(在 .NET 中使用 TCP)
(参阅其他 Siccolo 关于使用 .NET、Windows 事件日志、C# 和 VB.NET 的文章)
还有一些关于如何使用 ASP.NET 将事件日志条目“导出”到 RSS 源的想法 - 例如,事件日志 RSS 源生成器,或 使用 RSS 监视事件日志。 然而,在这个应用程序中,我使用 Windows 服务来监视 Windows 事件日志中与特定事件源相关的事件。
所以这个想法与 远程事件日志监视器/观察器(在 .NET 中使用 TCP) 非常相似 - 在一台机器上运行 Windows 服务,监视特定的事件日志条目并将它们导出到 RSS 源文件。 之后,任何支持 UNC 文件名的 Feed 阅读器和 RSS 聚合器都能够将这些事件日志条目显示为 RSS 源。 例如,我正在使用 用于 Outlook 的 intraVnews Feed 阅读器和 RSS 聚合器。

如你所知,.NET 允许开发人员附加一个“处理程序”来监视事件日志更改 (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
或者,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
}
}
这种方法的唯一问题是 - 它不允许监视远程机器上的事件日志更改。 请参阅 Microsoft 支持文章 815314。

1. 创建事件日志监视器服务
首先,让我们构建一个服务组件 - Windows 服务应用程序 - 负责“密切关注”机器上的事件日志。 要创建一个服务应用程序(即作为服务运行的应用程序)

作为服务运行的应用程序具有一些事件(继承自 System.ServiceProcess.ServiceBase
)
OnStart()
- 服务收到 Start 命令时发生OnStop()
- 服务收到 Stop 命令时发生OnPause()
和OnContinue()
- 服务收到 Pause/Resume 命令时发生
对于事件日志监视器服务,我们只需要 OnStart()
和 OnStop()
事件。onStart()
过程说明了当事件日志监视器服务开始运行时应采取的操作 - 加载配置设置(例如,RSS 源的输出位置;要监视的事件日志;是否需要过滤某些事件源和过滤某些事件类型 - 例如,我们可能只需要“注意”来自 Microsoft SQL Server 服务的错误事件类型)然后实际开始工作并监视事件日志更改!
例如,config 文件可能如下所示...

...其中配置设置是使用 ConfigurationManager
类加载的。 为了从配置文件中加载/读取设置,我使用了以下简单的类 LogWatcherSettings
(仅显示一个设置的一个方法)
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
}
}
...
...
}
}
现在,回到日志监视器主类 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
ArrayList m_EventLogsToMonitor = new ArrayList();
//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();
}
}
}
因此,正如你所看到的,日志监视器服务主类 log_watch_rss
并没有做很多工作 - 加载设置并创建 EventLogRSS
的实例。 EventLogRSS
类是监视 Windows 事件日志更改并将它们“导出”到 RSS 源文件的类。
2. 构建事件日志监视器/观察器
让我们看看 EventLogRSS
类。 首先,在类构造函数中
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();
//how many records before creating a new RSS file
int m_RecordsToPull=250;
int m_CurrentRecordCount = 0;
//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;
//construct 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();
}
}
因此,如果并且当一个新的条目被添加到 Windows 事件日志时
m_EventLog.EntryWritten += new EntryWrittenEventHandler(EventLog_OnEntryWritten);
m_EventLog.EnableRaisingEvents = true;
应用程序将调用 EventLog_OnEntryWritten()
过程
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);
}
}
并且,如果事件日志条目是针对给定的事件源和指定的事件类型,则调用 AddEntryToRSSFeed()
过程
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> ";
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);
}
}
就是这样。 我们刚刚开发了自己的 Windows 事件日志监视器/监控工具。 与 MOM 相比,你刚刚节省了,呃-h,大约 500 美元 .
历史
- 到目前为止没有改进,几乎完美