How to Build a Simple Event Log Monitor/Watcher (Using TCP in .NET)
Other articles about how to build a process (Windows application or Windows Service) to watch/monitor Windows Event Logs on a remote computer:
- Build a Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed using .NET System.Diagnostics.EventLog, or
- Use Windows Management Instrumentation (WMI)—Win32_NTLogEvent and ManagementEventWatcher—to Build a Windows Event Log Watcher Service Process to Export Event Log Entries as RSS feed.
.NET allows a developer to attach a “handler” to monitor for event log changes:
... 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
The only problem with this approach is that it does not allow you to monitor event log changes on a remote machine. See Microsoft support article 815314.
The following code allows you to build a simple event log “watcher” application to monitor event log changes on a remote machine(s).
Log Monitor Application (“Log Watcher”) consists of two components:
- Event Log Monitoring Service: Responsible for monitoring event log changes on a machine
- Centralized “Watcher Host”—Listener: Responsible for gathering data from log monitor services installed on different machines.
First, build a service component that will be responsible for “keeping an eye on” a machine’s event log. To create the service application (in other words, an application that runs as a service):
An application that runs as a service has a 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 this application, you’ll only need the OnStart() and OnStop() events.
... 'points to machine service is running on Private m_LocalIP As String = System.Net.Dns.GetHostName() Private m_Watcher_IP As String 'Listening Log Watcher Host IP Private m_Watcher_Port As String 'Listening Log Watcher Host Port Private m_ErrorLogFile As String 'Log file where we can log useful 'information while the service 'is running ... Protected Overrides Sub OnStart(ByVal args() As String) ' Add code here to start your service. This method should set ' things in motion so your service can do its work. 'open config file: LogMonitoringService.exe.config m_Watcher_IP = _ System.Configuration.ConfigurationSettings.AppSettings. _ Get("watcher_ip") m_Watcher_Port = _ System.Configuration.ConfigurationSettings.AppSettings. _ Get("watcher_port") m_ErrorLogFile = Path.Combine(GetApplicationDirectory(), _ "log_monitoring_service_errors.txt") WorkerThread = New Thread(AddressOf WatchEventLog) WorkerThread.Start() End Sub
where the config file looks like this:
So, when the service starts, you get configuration settings and start the event log monitor:
Public Sub WatchEventLog() Try m_LogWatcherLog = New EventLog() 'just to make sure we have LogMonitoringService source registered Try m_LogWatcherLog.CreateEventSource("LogMonitoringService", _ "Application") Catch End Try m_LogWatcherLog.Close() m_LogWatcherLog = New EventLog("Application", ".", _ "LogMonitoringService") m_LogWatcherLog.Source = "LogMonitoringService" 'make a record in Application log: m_LogWatcherLog.WriteEntry("LogWacther started." & vbCrLf & _ "Send data to [" & m_Watcher_IP & ":" & m_Watcher_Port & "]" _ & vbCrLf & _ "Error file [" & m_ErrorLogFile & "]", _ EventLogEntryType.Information) 'make a record in log file: LogError("LogWacther started." & vbCrLf & _ "Send data to [" & m_Watcher_IP & ":" _ & m_Watcher_Port & "]" & vbCrLf & _ "Error file [" & m_ErrorLogFile & "]") ' "attach" to Application Log m_ApplicationLog = New EventLog() m_ApplicationLog.Log = "Application" AddHandler m_ApplicationLog.EntryWritten, _ AddressOf ApplicationLog_OnEntryWritten m_ApplicationLog.EnableRaisingEvents = True ' "attach" to System Log m_SystemLog = New EventLog() m_SystemLog.Log = "System" AddHandler m_SystemLog.EntryWritten, _ AddressOf SystemLog_OnEntryWritten m_SystemLog.EnableRaisingEvents = True m_run = True Do While (m_run) Thread.Sleep(10000) Loop Catch e As Exception Dim Log As New EventLog("Application") Log.WriteEntry("Failed to WatchEventLog:" & e.ToString, EventLogEntryType.Error) Log.Close() Log.Dispose() End Try End Sub
Now, as soon as a change in the Application or System event logs is detected…
Public Sub ApplicationLog_OnEntryWritten(ByVal [source] As Object, _ ByVal e As EntryWrittenEventArgs) Try LogError("Application Log Entry:" & vbCrLf & "Message _ [" & e.Entry.Message & "]") SendEventLogEntryToHost("Application", e.Entry) Catch err As Exception LogError("Failed to ApplicationLog_OnEntryWritten:" & _ err.ToString()) End Try End Sub Public Sub SystemLog_OnEntryWritten(ByVal [source] As Object, _ ByVal e As EntryWrittenEventArgs) Try LogError("System Log Entry:" & vbCrLf & "Message _ [" & e.Entry.Message & "]") 'send data to watcher SendEventLogEntryToHost("System", e.Entry) Catch err As Exception LogError("Failed to SystemLog_OnEntryWritten:" & err.ToString()) End Try End Sub
… the application will “contact” watching the host and send log entry data:
Private Function SendEventLogEntryToHost(ByVal LogName As String, _ ByVal e As EventLogEntry) As Boolean Try Dim objTCP As Socket Dim remoteEndPoint As New IPEndPoint( _ IPAddress.Parse(m_Watcher_IP), m_Watcher_Port) objTCP = New Socket( _ remoteEndPoint.Address.AddressFamily, SocketType.Stream, _ ProtocolType.Tcp) objTCP.Connect(remoteEndPoint) If objTCP.Connected Then LogError("objTCP socket connected to _ [" & remoteEndPoint.Address.ToString() & "]" & vbCrLf & _ " From Port [" & CType(objTCP.LocalEndPoint, IPEndPoint). _ Port.ToString() & "]") 'send data to watcher host: Dim Message As String = _ e.TimeGenerated.ToString("MM/dd/yyyy HH:mm:ss") & "|" & _ LogName & "|" & _ e.EntryType.ToString() & "|" & _ e.Message Dim sendBytes As Byte() = _ System.Text.Encoding.ASCII.GetBytes(Message) objTCP.Send(sendBytes, sendBytes.Length, SocketFlags.None) LogError("objTCP socket sent [" & sendBytes.Length & "] bytes") Else LogError("objTCP socket did not connected to _ [" & remoteEndPoint.Address.ToString() & "]") End If objTCP.Shutdown(SocketShutdown.Both) objTCP.Close() LogError("TCP client closed") Catch err As Exception LogError("Failed to SendEventLogEntryToHost:" & err.ToString()) End Try End Function
To make life easier, the service application sends the event log entry as one string:
... Dim Message As String = _ e.TimeGenerated.ToString("MM/dd/yyyy HH:mm:ss") & "|" & _ LogName & "|" & _ e.EntryType.ToString() & "|" & _ e.Message Dim sendBytes As Byte() = _ System.Text.Encoding.ASCII.GetBytes(Message) objTCP.Send(sendBytes, sendBytes.Length, SocketFlags.None) ...
And the host will “split” the string into corresponding fields.