How to Build a Simple Event Log Montior/Watcher (Using TCP in .NET)

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:

.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.

How to Build a Simple Event Log Montior/Watcher (Using TCP in .NET)

The second part will build the actual "watch dog" that will receive notifications from event log watchers. For this, a regular Windows application will do just fine. The idea is simple: Start the TCP listener and wait for data from the event log watchers.

   ...
   ...
Private m_ListenerMonitorPort As Integer
Friend m_objListener_Monitor As TcpListener
   ...
   ...

Private Sub frmLogMonitor_Load(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) _ 
   Handles MyBase.Load

   Me.Cursor = Cursors.WaitCursor

   m_ListenerMonitorPort = System.Configuration._
      ConfigurationSettings.AppSettings.Get("watcher_port")
   m_Notify = System.Configuration.ConfigurationSettings._
      AppSettings.Get("notify")

   lblPort.Text = "Listening for changes on port _
      [" & m_ListenerMonitorPort & "]"
   lblNotify.Text = "Notify on change - [" & m_Notify & "]"

   'attach event handler: so we can monitor local events
   'we cannot monitor events on remote computer this way:
   'http://support.microsoft.com/?scid=kb;EN;815314
   'Receive Event Notifications
   'You can receive event notification when an entry is written to
   'a particular log. To do this, implement the EntryWritten event
   'handler for the instance of the EventLog.
   'Also, set EnableRaisingEvents to true.
   'Note You can only receive event notifications when entries are
   'written on the local computer.
   'You cannot receive notifications for entries that are written
   'on remote computers.
   ' to monitor local event log:
   Dim objLog As EventLog = New EventLog("Application")
   AddHandler objLog.EntryWritten, _
      AddressOf ApplicationLog_OnEntryWritten
   objLog.EnableRaisingEvents = True

   'remember form
   m_FormSize = Me.Size()
   m_FormLocation = Me.Location

   UpdateApplicationStatus("Application started. Port [" & _
      m_ListenerMonitorPort & "]. Notify [" & m_Notify & "]")

   Me.Cursor = Cursors.Default
End Sub

where the configuration file is logmonitor.exe.config:

and the UpdateApplicationStatus() method simply adds application events to the list box:

Friend Sub UpdateApplicationStatus(ByVal Message As String)
   Message = System.DateTime.Now.ToString("HH:mm:ss") & " - " _
      & Message
   lstApplicationEvents.Items.Add(Message)
End Sub

To start monitoring process:

Private Sub cmdStartListener_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) _
   Handles cmdStartListener.Click
   ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf _
      ListenForWatchers), Me)
End Sub

Where ListenForWatchers() opens a port for the listener and waits for incoming connections from the "log watcher" (entries from remote event logs go into the top list view control on the form):

Public Sub ListenForWatchers(ByVal objState As Object)
   Dim objUI As frmLogMonitor

   Try
      objUI = CType(objState, frmLogMonitor)

      m_objListener_Monitor = New TcpListener(m_ListenerMonitorPort)
      m_objListener_Monitor.Start()

      objUI.UpdateApplicationStatus("Started listening on port [" & _
         m_ListenerMonitorPort.ToString() & "]")

      Do
         Dim objClient As New Socket(AddressFamily.InterNetwork, _
            SocketType.Stream, ProtocolType.Tcp)
         objClient = m_objListener_Monitor.AcceptSocket()

         Dim remoteEndPoint As IPEndPoint = _
            CType(objClient.RemoteEndPoint, IPEndPoint)
         objUI.UpdateApplicationStatus("TCP connection from [" & _
            remoteEndPoint.Address.ToString() & ":" & _
            remoteEndPoint.Port.ToString() & "]")

         Do While objClient.Available = 0
            'wait...
            If Not objClient.Connected Then
               'oops...we lost it...
               Throw New System.Exception("!Did not receive _
                                          data!Or Not Connected")
            End If
         Loop

         If objClient.Available > 0 Then
            Dim InBytes(objClient.Available) As Byte
            objClient.Receive(InBytes, objClient.Available, _
                              SocketFlags.None)
            Dim Message As String = _
               Replace(System.Text.Encoding.ASCII.GetString(InBytes), _
               Chr(0), "")

            Dim EventLogEntry() As String = Message.Split("|")
            Dim date_time As String = _
               System.DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss")
            Dim objItem As ListViewItem = _
               lvwLogEntries.Items.Add(date_time)
            With objItem
               .SubItems.Add(remoteEndPoint.Address.ToString())
               .SubItems.Add(EventLogEntry(1))
               .SubItems.Add(EventLogEntry(2))
               .SubItems.Add(EventLogEntry(3))
            End With
         Else
               objUI.UpdateApplicationStatus("no data received from _
                  TCP connection [" & _
         remoteEndPoint.Address.ToString() & ":" & _
            remoteEndPoint.Port.ToString() & "]")
         End If
      Loop Until False


   Catch err As Exception
      objUI.UpdateApplicationStatus("ListenForWatchers():Process _
         TcpSocket Error [" & err.Message & "] ")
   End Try
End Sub

and entries from local event logs go into the bottom list view control on the form:

Private Sub frmLogMonitor_Load(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) _
   Handles MyBase.Load
   ...
   ...
   Dim objLog As EventLog = New EventLog("Application")
   AddHandler objLog.EntryWritten, _
      AddressOf ApplicationLog_OnEntryWritten
   objLog.EnableRaisingEvents = True
   ...
   ...
End Sub

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

      Dim date_time As String = _
         e.Entry.TimeGenerated.ToString("MM/dd/yyyy HH:mm:ss")
      Dim objItem As ListViewItem = _
         lvwLogEntries_OnEntryWritten_Handler.Items.Add(date_time)
      With objItem
         .SubItems.Add(e.Entry.MachineName.ToString())
         .SubItems.Add("Application")
         .SubItems.Add(e.Entry.EntryType.ToString())
         .SubItems.Add(e.Entry.Message)
      End With

   Catch err As Exception
      MessageBox.Show("Failed to process entry:" & vbCrLf & _
         "-----------------------------------" & vbCrLf & _
         err.Message & vbCrLf & _
         "-----------------------------------", _
         "OnEntryWritten Handler", _
         MessageBoxButtons.OK, _
         MessageBoxIcon.Exclamation)
   End Try
End Sub

And that's it!

The application can be extended to monitor only certain events—such as Errors only, and/or events from certain Event sources only—for example, from MS SQL Server only. To do this, you need to change the code in the Log Monitoring service just a little bit:

Public Sub ApplicationLog_OnEntryWritten(ByVal [source] As Object, _
   ByVal e As EntryWrittenEventArgs)
   Try
      If e.Entry.EntryType = EventLogEntryType.Error And _
         e.Entry.Source = "MSSQLSERVER" Then

         LogError("Application Log Entry:" & vbCrLf & "Message _
            [" & e.Entry.Message & "]")

         SendEventLogEntryToHost("Application", e.Entry)
      End If

   Catch err As Exception
      LogError("Failed to ApplicationLog_OnEntryWritten:" _
      & err.ToString())
   End Try
End Sub

The application also can send notifications not to just one log watching host, but to multiple ones. In this case, you need to modify SendEventLogEntryToHost() to pass additional parameter: host address (and, of course, you can add Monitoring Host Port as well):

Private Function SendEventLogEntryToHost(ByVal LogName As String, _
   ByVal MonitoringHost As String, _
   ByVal e As EventLogEntry) As Boolean
   Try
      'send data to watcher
      Dim objTCP As Socket
      Dim remoteEndPoint As New IPEndPoint(IPAddress._
         Parse(MonitoringHost), _
         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

If you want to read more on this story, please take a look at Siccolo - Free Mobile Management Tool For SQL Server and more articles at Siccolo Articles.



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

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds