Eventlog application error

Explains how to handle ASP.NET errors on application level with Global.asax or by using a custom http module.

ASP.NET provides many different ways for error handling. Error handling starts on page level with try-catch blocks and Page_Error procedure. To find out more about this see Errors and Exceptions in ASP.NET tutorial. This tutorial goes one step further and explains error handling on Application level.
This includes handling errors with Application_Error procedure in Global.asax or by
using custom http module. These methods are usually used to log error details in
text file, database or Windows EventLog, or to send notification e-mails to
administrator.

Handling errors in Application_Error in Global.asax

Global.asax is optional file. Your site can work without it, but it could be very
useful. You can have only one Global.asax in the root
folder of your web site. Global.asax contains Application_Error procedure which
executes whenever some unhandled error occurs. By using Application_Error, you
can catch all unhandled errors produced by your web site, and then write a code to save error
messages to database, text file or Windows EventLog, send notificationn e-mail, write some message to user, redirect user
to other page with Response.Redirect etc. If you used Server.ClearError() in
Page_Error procedure, Application_Error in Global.asax will not execute. Implementation code is
similar to Page_Error code above:

[ C# ]


void

Application_Error(object sender,
EventArgs e)



{


  // Get
current exception



 
Exception
CurrentException = Server.GetLastError();


  string
ErrorDetails = CurrentException.ToString();
433333

 


  // Now do
something useful, like write error log


  // or
redirect a user to other page



  …

}

[ VB.NET ]


Sub

Application_Error(ByVal sender
As Object,
ByVal e As
EventArgs)


  ‘ Get
current exception



  Dim
CurrentException As Exception =
Server.GetLastError()


  Dim
ErrorDetails As String
= CurrentException.ToString()


 


  ‘ Now do
something useful, like write error log


  ‘ or
redirect a user to other page



  …


 

End Sub

Writing errors to EventLog

One of the ways to track errors is to write them to EventLog. If your web site is
on shared hosting you probably can’t write to EventLog because of security
issues. If you are on dedicated server, you need to enable «Full control» to
EventLog for ASPNET account. You can do this in Registry Editor. Go to Start
menu -> Run… and type regedt32 or regedit and press Enter. Registry Editor
will show. Go to HKEY_LOCAL_MACHINESystemCurrentControlSetServicesEventLog, right mouse click and select Permissions… from context menu, like in image bellow.

Registry editor
Registry editor

Click on Permissions… item to show a dialog. Give Full Control to your ASP.NET Machine Account, like in next image (include child objects too!).

Registry permissions dialog
Registry permissions dialog

Now ASPNET account has enough rights. To write in EventLog we can use Try…Catch syntax or Page_Error event, but to avoid duplication of code better option is to use Application_Error in Global.asax file. Code inside Application_Event procedure will be executed every time when some error occurs on web site.

[ C# ]


void

Application_Error(object sender,
EventArgs e)



{


  // Get
current exception



 
Exception
CurrentException = Server.GetLastError();


 


  // Name
of the log


  string
SiteLogName = «My Web Site Errors»;

   


  // Check
if this log name already exists


  if
(EventLog.SourceExists(SiteLogName) ==
false)


  {


   
EventLog.CreateEventSource(SiteLogName,
SiteLogName);


  }


 


  // Write
new error log


 
EventLog
NewLog = new
EventLog();


  NewLog.Source = SiteLogName;


 
NewLog.WriteEntry(CurrentException.ToString(),
EventLogEntryType
.Error);

}

[ VB.NET ]


Sub
Application_Error(ByVal sender
As Object,
ByVal e As
EventArgs)


  ‘ Get
current exception



  Dim
CurrentException As Exception =
Server.GetLastError()


 


  ‘ Name of
the log


  Dim
SiteLogName As String
= «My Web Site Errors»

  


  ‘ Check
if this log name already exists


  If
EventLog.SourceExists(SiteLogName) = False
Then


    EventLog.CreateEventSource(SiteLogName,
SiteLogName)


  End
If

 

  ‘ Write
new error log


  Dim
NewLog As EventLog =
New
EventLog()


  NewLog.Source = SiteLogName


 
NewLog.WriteEntry(CurrentException.ToString(), _  



   
EventLogEntryType.Error)

End Sub

Reading web site error messages from EventLog

After we created procedure for writing site error messages to Windows EventLog, we need to read them too. You can read these messages by using Windows Event Viewer, located in Control Panel. Also, you can show these messages on web page in some kind of report. To read EventLog error messages with ASP.NET
and show them on page you can use code like this:

[ C# ]


using

System;


 

// We need these namespace to read EventLog

using

System.Diagnostics;


 


public

partial class
_Default : System.Web.UI.Page



{


  protected
void Page_Load(object
sender, EventArgs e)


  {


   
// Load errors to EventLog object


   
EventLog MySiteErrorLogs =
new EventLog(«My Web Site Errors»);


 


   
foreach(EventLogEntry
SiteErrorLog in MySiteErrorLogs.Entries)


    {


     
// Find when error occured


     
Response.Write(«Time generated: « +


       
SiteErrorLog.TimeGenerated.ToString() + «<br />»);


     
// Show error details


     
Response.Write(SiteErrorLog.Message +
«<hr />»);


    }


  }

}

[ VB.NET ]


Imports

System


 

‘ We need these namespace to read EventLog

Imports

System.Diagnostics


 


Partial

Class DefaultVB


  Inherits
System.Web.UI.Page


 


 Protected
Sub Page_Load(ByVal
sender As Object,
ByVal e As
System.EventArgs) Handles
Me.Load


  
Load errors to EventLog object


   Dim
MySiteErrorLogs As EventLog =
New EventLog(«My Web
Site Errors»
)


 


   For
Each SiteErrorLog In
MySiteErrorLogs.Entries


   
‘ Find when error occured


    Response.Write(«Time generated: « & _


      
SiteErrorLog.TimeGenerated.ToString() & «<br />»)


   
‘ Show error details


   
Response.Write(SiteErrorLog.Message + «<hr />»)


   Next


 End
Sub


End

Class

Logging of unhandled errors to text file or database

On the same way, we can use Application_Error in Global.asax to write error message to text file or
in some table in database.
In this example, Application_Error procedure contains a code that writes error information to
.txt file.

[ C# ]

void Application_Error(object sender, EventArgs e)
{
  // Get current exception
  Exception CurrentException;
  CurrentException = Server.GetLastError();
 
  // Write error to text file
  try
  {
    string LogFilePath = Server.MapPath(«ErrorLog.txt»);
    StreamWriter sw = new StreamWriter(LogFilePath);
    // Write error to text file
    sw.WriteLine(CurrentException.ToString());
    sw.Close();
  }
  catch (Exception ex)
  {
    // There could be a problem when writing to text file
  }
}

[ VB.NET ]

Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
 
  ‘ Get current exception
  Dim CurrentException As Exception
  CurrentException = Server.GetLastError()
 
  ‘ Write error to text file
  Try
   
    Dim LogFilePath As String = Server.MapPath(«ErrorLog.txt»)
      
    Dim sw As System.IO.StreamWriter = _
      New System.IO.StreamWriter(LogFilePath)
    ‘ Write error to text file
    sw.WriteLine(CurrentException.ToString())
    sw.Close()
  Catch ex As Exception
    ‘ There could be a problem when writing to text file
  End Try
End Sub

Better error logging with Log4Net

If you simply write error details to text file you will get simple, but not
scalable solution. In case of large number of concurrent users, text file could
be locked for writing and you will get another error. Log4Net is more scalable
solution for this problem. To find out how to use Log4Net see Using NHibernate and Log4Net in ASP.NET 2.0 applications tutorial.

How to send e-mail with error details

You can place a procedure for sending an email inside Application_Error,
try-catch block or any other error handling code. Function to send email is
pretty simple. In this example, code for sending notification e-mails is located
in Application_Error procedure in Global.asax file. On this way e-mail will be
sent whenever some undhandled error occurs.

[ C# ]


<%
@
Application
Language=»C#»
%>



<%
— We need these namespaces to
send e-mail —
%>

<%
@
Import
Namespace=»System.Net»
%>


<%
@
Import
Namespace=»System.Net.Mail»
%>


<
script runat=»server»>

 


void

Application_Error(object sender,
EventArgs e)


{


   
// Get current exception



   
Exception CurrentException =
Server.GetLastError();


   
string ErrorDetails =
CurrentException.ToString();


 


   
// Send notification e-mail


   
MailMessage Email =



       
new MailMessage(«admin@yoursite.com»,


       
«admin@yoursite.com»);


    Email.IsBodyHtml =
false;


    Email.Subject =
«WEB SITE ERROR»;


    Email.Body =
ErrorDetails;


    Email.Priority =
MailPriority.High;


   
SmtpClient sc = new
SmtpClient(«localhost»);


    sc.Credentials =



       
new
NetworkCredential
(«EMailAccount»,
«Password»);


    sc.Send(Email);


}   


      


</
script>

[ VB.NET ]


<%
@
Application
Language=»VB»
%>


 


<%
— We need these namespaces to
send e-mail —
%>

<%
@
Import
Namespace=»System.Net»
%>


<%
@
Import
Namespace=»System.Net.Mail»
%>


 


<
script runat=»server»>
       


Sub

Application_Error(ByVal sender
As Object,
ByVal e As
EventArgs)


   
‘ Get current exception



   
Dim CurrentException As
Exception = Server.GetLastError()


   
Dim ErrorDetails As
String = CurrentException.ToString()


 


   
Send notification e-mail


    Dim
Email As MailMessage = _


       
New MailMessage(«admin@yoursite.com»,
_


       
«admin@yoursite.com»)


    Email.IsBodyHtml =
False


    Email.Subject =
«WEB SITE ERROR»


    Email.Body =
ErrorDetails


    Email.Priority =
MailPriority.High


   
Dim sc As
SmtpClient = New SmtpClient(«localhost»)


    sc.Credentials = _


   
New NetworkCredential(«EMailAccount»,
«Password»
)


    sc.Send(Email)


End

Sub

      

</script>

Handling errors with custom HttpModule

Custom Http module advantage is that module doesn’t require changes in your web
application code. Http module will catch the same event like Application_Error
procedure. To create custom Http Module, start new project in Visual Studio. Project type should be Class Library. Add code like this:

[ C# ]

using System;
using System.Web;
 
namespace CommonModules
{

public class ErrorHandlingModule : IHttpModule
{
 
  public void Init(HttpApplication app)
  {
    app.Error += new EventHandler(this.HandleErrors);
  }
 
  public void HandleErrors(object o, EventArgs e)
  {
    // Get current exception
    Exception CurrentException = ((HttpApplication)o).Server.GetLastError();
 
    // Now do something with exception with code like in examples before,
    // send e-mail to administrator,
    // write to windows EventLog, database or text file
  }
 
  public void Dispose()
  {
    // We must have this procedure to implement IHttpModule interface
  }
}
}

[ VB.NET ]

Imports System
Imports System.Web
 
Namespace CommonModules

Public Class ErrorHandlingModule
  Implements IHttpModule
 
  Public Sub Init(ByVal app As HttpApplication)
    app.Error += New EventHandler(this.HandleErrors)
  End Sub
 
  Public Sub HandleErrors(ByVal o As Object, ByVal e As EventArgs)
    ‘ Get current exception
    Dim CurrentException As Exception = (CType(o, _
     HttpApplication)).Server.GetLastError()
 
    ‘ Now do something with exception with code like in examples before,
    ‘ send e-mail to administrator,
    ‘ write to windows EventLog, database or text file
  End Sub
 
  Public Sub Dispose()
    ‘ We must have this procedure to implement IHttpModule interface
  End Sub
End Class

End Namespace

To use the module in web application, you need to add few lines in Web.Config file.

<httpModules>
  <add name=«ErrorHandlingModule» type=«CommonModules.ErrorHandlingModule, CommonModules «/>
</httpModules>

More about how to create and implement custom Http modules in ASP.NET see in How To Create Your Own Http Module tutorial.

ASP.NET Error Handling Remarks

Be careful to handle possible errors in your Application_Error procedure. This
procedure is executed when every error occurs, so Application_Error will call
itself and it is possible to go in to infinite loop.

Writing error logs to text file can cause a problem with concurrency if a lot of
errors need to be logged simultaneously. To make this task easier, you can use Log4Net. Also, there is pretty nice complete solution called Elmah now available at Google code.


Tutorial toolbar:  Tell A Friend  |  Add to favorites  |  Feedback  |   Google


By Jonathan Goodyear, Brian Peek, and Brad Fox

This article is brought to you by New Riders Publishing, publishers of Debugging ASP.NET



The integration infrastructure consists of a set of middleware technologies that provide the interoperability services necessary to establish communication between applications. This provides the foundation on which we define the integration architecture.


Global Exception Handling

You can’t debug a problem if you don’t know that it exists. After
you take your web application live, you are no longer the only one who is using it (hopefully), so you need an effective plan to track exceptions when they occur while others are surfing your site. A great way to do this is to implement an exception handler at the application level. This will allow you to consolidate the logging and notification parts of your exception handling in one convenient place. As you’ll see from the code examples that follow, your global exception handler can handle both specific exceptions that you trap in
your code and generic unhandled exceptions.

After your global exception handler has done its work, you’ll want to
redirect the users of your website to a friendly page that tells them that
something has gone wrong, and then provide them with customer support
information as well as a link back to your web application’s home page.

Implementing the Application_Error Event Handler

The HttpApplication class in the System.Web namespace implements an
Error event handler. This should not be confused with the
HttpApplicationState class, which contains the definition for the
Application object that you use in a typical ASP.NET page. You can
implement this event handler in the global.asax file as shown in Listings 1 and
2.

Listing 1: Application_Error Event Handler

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>

<script language="C#" runat="server">
void Application_Error(object sender, EventArgs e)
{
   //get reference to the source of the exception chain
   Exception ex = Server.GetLastError().GetBaseException();

   //log the details of the exception and page state to the
   //Windows 2000 Event Log
   EventLog.WriteEntry("Test Web",
     "MESSAGE: " + ex.Message + 
     "nSOURCE: " + ex.Source +
     "nFORM: " + Request.Form.ToString() + 
     "nQUERYSTRING: " + Request.QueryString.ToString() +
     "nTARGETSITE: " + ex.TargetSite +
     "nSTACKTRACE: " + ex.StackTrace, 
     EventLogEntryType.Error);

   //Insert optional email notification here...
}
</script>

Listing 2: Application_Error Event Handler (VB)

<%@ Application Language="VB" %>
<%@ Import Namespace="System.Diagnostics" %>

<script language="VB" runat="server">
Sub Application_Error(sender As Object, e As EventArgs)
   'get reference to the source of the exception chain
   Dim ex As Exception = Server.GetLastError().GetBaseException()

   'log the details of the exception and page state to the
   'Windows 2000 Event Log
   EventLog.WriteEntry("Test Web", _
     "MESSAGE: " & ex.Message & _
     "nSOURCE: " & ex.Source & _
     "nFORM: " & Request.Form.ToString() & _
     "nQUERYSTRING: " & Request.QueryString.ToString() & _
     "nTARGETSITE: " & ex.TargetSite & _
     "nSTACKTRACE: " & ex.StackTrace, _
     EventLogEntryType.Error)

   'Insert optional email notification here...
End Sub
</script>

First, you have to be sure to set a reference to the System.Diagnostics
namespace. You’ll use the EventLog class in this namespace to
write exception details to the Windows 2000 event log. Inside the
Application_Error event handler, you declare an Exception
object and initialize it through a call to
Server.GetLastError().GetBaseException().

The GetLastError() method of the Server object simply
returns a reference to a generic HttpException. This is a wrapper that
was placed around the original exception when it was passed from your ASP.NET
page to the Application_Error event. To get access to the original
exception, you need to call its GetBaseException() method. This will
yield the original exception information, regardless of how many layers have
been added to the exception tree.

Next, you make a call to the WriteEntry() method of the
EventLog class. There are several overloaded signatures for this
method. The implementation that we chose to use here accepts three parameters.
The first parameter is the source of the error. It appears in the Source field
of the Windows 2000 event log viewer. The second parameter is the log data
itself. You can see that we have added a lot of information to help track down
what caused the exception, including the exception message, the exception
source, the contents of the Form collection, the contents of the
QueryString collection, the name of the method that generated the error
(TargetSite), and a complete stack trace.

Note that the stack trace contains the name of the file that was the source
of the exception. However, it strips off the contents of the query
string—hence the need to specifically include it previously. The third and
final parameter to the WriteEntry() method is an enumeration of type
EventLogEntryType. We chose to use the Error element of the
enumeration.

At the end of the event handler, we inserted a comment block where you can
optionally put code to email the exception information to your IT support staff.
Discussion of the different messaging paradigms in the .NET framework is beyond
the scope of this article.

After the Application_Error event has completed its work, it
automatically redirects the user of your web application to your custom error
page. Optionally, however, you can use the Server.ClearError() method
after you have logged the exception and redirect your user using the
Server.Execute() method, specifying the page that you want to load in
the user’s browser.

The code that you have just implemented will capture all unhandled exceptions
that occur in your web application. If you need to do some cleanup in the event
of an exception and you implement structured exception handling inside your
ASP.NET page, you can still leverage the global exception handler. Listings 3
and 4 present examples of how you would do it.

Listing 3: Throwing a Handled Exception

<%@ Page Language="C#" %>

<script language="C#" runat="server">
protected void button1_click(object sender, EventArgs e)
{
   try
   {
     //do some complex stuff

     //generate your fictional exception
     int x = 1;
     int y = 0;
     int z = x / y;
   }
   catch(DivideByZeroException ex)
   {
     //put cleanup code here
     throw(ex);
   }
}
</script>

<form runat="server">
   <asp:button id="button1" onclick="button1_click"
     text="click me" runat="server" />
</form>

Listing 4: Throwing a Handled Exception (VB)

<%@ Page Language="VB" %>

<script language="VB" runat="server">
Protected Sub button1_click(sender As Object, e As EventArgs)
   Try
     'do some complex stuff

     'generate your fictional exception
     Dim x As Integer = 1
     Dim y As Integer = 0
     Dim z As Integer = x / y
   Catch ex As DivideByZeroException
     'put cleanup code here
     Throw(ex)
   End Try
End Sub
</script>

<form runat="server">
   <asp:button id="button1" onclick="button1_click"
     text="click me" runat="server" />
</form> 

The code in these listings defines a web form with a text box and a button.
When you click the button, it fires the button1_click event handler. In
the event handler, you would do processing as usual. For the purposes of this
demonstration, however, you intentionally generate a
DivideByZeroException. This takes you to the catch block. Here, you can
perform any page-specific cleanup code before calling throw(ex) to pass
your exception to the global exception handler to be logged to the Windows 2000
event log.

When the global exception handler is finished logging the error, the
defaultredirect attribute that you set in your config.web file
(discussed in the next section) takes over, and you are redirected to the
error.aspx page to display your friendly message to the user of your web
application.

Setting Up the Custom Error Page

The first step in setting up a custom error page is to modify your config.web
file to route the users of your web application to a friendly error page if an
exception occurs. It helps to boost users’ confidence in your site when it
can recover gracefully from the unexpected. Add the code in Listing 5 to the
config.web file of your web application.

Listing 5: Adding the <customerrors> Tag to Your Config.web
File

<configuration>
  <customerrors mode="On" defaultredirect="error.aspx" />
</configuration>

Note that your config.web file might already have a
<customerrors> tag, so you might only need to modify the existing
one. The mode attribute of the <customerrors> tag has
three settings: On, Off, and RemoteOnly. If the mode
is On, users will always be redirected to the custom error page
specified by the defaultredirect attribute if an unhandled exception
occurs. If the mode is Off, the details of any exception that occurs
will be shown to the user in the browser. The RemoteOnly mode is a
hybrid of the two other modes. If you are browsing your web application while
sitting at the web server itself, it behaves like the Off mode. All
other browsers of the site will get the behavior of the On mode. If no
defaultredirect attribute is set, a default ASP.NET «friendly, yet
not so friendly» message will be displayed to the user when exceptions
occur.

Next, you need to build the custom error page (error.aspx) referenced in the
config.web file. This is just an ordinary ASP.NET page that includes helpful
information for the user of your web application if an error occurs. An
extremely simple example is the one in Listing 6.

Listing 6: Simple Custom Error Page

<html>
<head>
<title>My web application: Error page</title>
</head>
<body>
An unexpected error occurred in the application. Please contact [ccc]
customer service at (800)555-5555. Or, you can click [ccc]
<a href="home.aspx">here</a> to go back to the homepage. [ccc]
Thank you for your patience.
</body>
</html>

About This Article

This article is excerpted from Debugging ASP.NET by Jonathan Goodyear, Brian Peek, and Brad Fox (New Riders Publishing
(http://www.newriders.com),
2001, ISBN 0-7357-1141-0). Refer to Chapter 4, «Code Structure That Eases
Debugging,» for more detailed information on the material covered in this
article.

© Copyright New Riders Publishing, All Rights Reserved

# # #

DBAs regularly need to keep an eye on the error logs of all their SQL Servers, and the event logs of the host servers as well. When server numbers get large, the traditional Windows GUI approach breaks down, and the PoSH DBA reaches for a PowerShell script to do the leg-work.

“I know I’m searching for something
Something so undefined
that it can only be seen
by the eyes of the blind
in the middle of the night.”

Billy Joel

Contents

  • Reading the Windows Event Viewer 2
    • Get-EventLog examples.
      • Getting entries from the Windows Error Log into Excel 3
      • Listing the last day that an entry was made in the Application Event Log
      • Listing the System Event Log for the past two hours. 4
      • listing the Event Log between two time-periods. 4
      • Filtering the error log by the Error types. 5
      • Reading errors from just one particular source. 6
      • Reading all messages containing a specific string. 6
      • Selecting events according to a variety of conditions. 6
      • Selecting the event logs of a number of servers and instances?.
  • Reading the SQL Server Error Log. 7
    • Accessing SQL Error logs in Online SQL Server Instances. 8
    • Accessing SQL Error logs in OffLine SQL Server Instances. 10
  • Applying Filters to the SQL Error Log. 13
    • SQL Error Log in Online SQL Server Instances. 13
    • SQL Error Log in Offline SQL Server Instances. 19
  • Summary. 22

Introduction

One of the everyday tasks of any DBA is to look for errors in your database server environment. With SQL Server, we have two major sources of information for doing this: the SQL Server Error Log and the Event Viewer.

When a problem occurs in SQL Server, ranging from a logon failure to a severe error in database mirroring, the first place to look for more information is the SQL Server Error Log. Similarly, if we have a problem related to physical hardware, the disk for example, we will look in the Event Viewer.

Both the SQL Server Error Log and the Event Viewer are designed to be used via their respective graphic user interface. This is fine for one or two servers, but painfully slow for the average DBA who has to read and filter information in many servers. Even when you’re focusing down on a problem with a single busy server, the added weight of the graphical tool in terms of resources can slow troubleshooting down considerably. It is very important in the day-to-day life of a DBA to have a mechanism to read and filter error messages quickly and unintrusively; a technique for “mining errors”.

This is where PowerShell comes in handy. With a relatively simple script, you can read, and filter out just those error messages that you need in a multi-server environment and moreover, format the output to make the information stand out. In this article we will show how to do this, and, if required, include warnings or any other type of event, using the SQL Server Error Log in both an Online and Offline mode as well as messages in the Windows Event Viewer.

Reading the Windows Event Viewer

We are going to want to check the server logs automatically for problems or warnings. If, unlike us, you have the time to routinely ‘remote’ into each server in turn, then the Windows Event Viewer is the classic way of reading this information.

The official documentation states: “Windows Event Viewer is a utility that maintains logs about program, security, and system events on your computer. You can use Event Viewer to view and manage the event logs, gather information about hardware and software problems, and monitor Windows security events.” In other words, the event viewer collects the information about the health of your system.

Every process that starts within the Windows OS opens a communication channel with the OS informing it of its most important actions and events. This means, for example, that if the disk subsystem has a problem or if a service stops, this fact will be viewable in the Windows Event Viewer. In the same way, every SQL Server error message with a severity of 19 or greater is logged in both the SQL Server Error Log and the Event Viewer. Therefore, it’s important to have a mechanism to constantly monitor/read the Event Viewer, especially remotely, so you can find information about problems and take any necessary action; perhaps to even prevent a system crash.

PowerShell has a built-in cmdlet to make it easier to access information recorded in Event Viewer, but before we use it, let’s discuss some basic concepts that will help us to understand how to use it better.

The Event Viewer is a repository for the event logs. With the Event Viewer we can monitor the information about security, and identify hardware, software and system issues. There are three basic Event Logs:

  • System Log: Stores the events related to the Operational System, as a problem with a driver.
  • Application Log : Stores the events related to the Applications and programs
  • Security Log : Stored the events related to security, as invalid logon attempts

You can also create a custom event log. There are several third-party tools that have their own event log.

The built-in PowerShell cmdlet to access the Event Viewer is Get-EventLog. Figure 7 shows the output when using Get-EventLog to read the application event log:

Get-EventLog   -LogName   Application

1921-clip_image001-635x158.png

Figure1 – Properties from Get-EventLog

The Get-EventLog cmdlet has a parameter that allows you to read the Events remotely by passing in the name of the Server. Here we are using Get-EventLog to read the Security log on server ObiWan:

Get-EventLog  -ComputerName  ObiWan  -LogName  Security  

Get-EventLog examples

Getting entries from the Windows Error Log into Excel

Two weeks ago your company bought a monitoring software for the SQL Server Servers called ContosoMonitor and installed the agent on all servers. This morning you realize that The Servers are not sending monitoring messages. In the installation manual says that every event is recorded by the software in the local Event Viewer, at the Application log but with a specific source named ContosoMonitor. You decide to check the Event Viewer for all servers and look for errors from the installed agents, again exporting the output to an Excel spreadsheet with the Servers split into worksheets . You open a PowerShell session from your desktop and type :

Get-Content c:tempServers.txt | ForEach-Object { #A

       Get-Eventlog -ComputerName $_ -LogName Application  -EntryType Error -After (Get-Date).adddays(-1) |

       Sort-Object Time -descending |

       Export-Xls c:tempContosoMonitorError.xlsx -AppendWorksheet -WorksheetName $_ #B

}

#A — Loop in the Servers inside the file Servers.txt

#B — Filter the Event log in the current server of the loop, sorting by descending Date Time  and exporting to a xlsx splitting the servers in worksheets

To perform this operation using the Event Viewer GUI, You will need to connect to each Server and filter the Event Viewer by GUI, export to CSV file…etc. It is a painful process that will leave us wishing we could do it with two command lines of PowerShell. Let’s talk a little more about the PowerShell solution.

In order to read the Event Viewer, PowerShell has a built-in Cmdlet called Get-EventLog. There are some parameters in the Get-EventLog that can perform the filtering operation without needing an additional Where-Object and using it is faster than using the pipeline. Let’s take a look.

Note You can check out a complete help by typing Get-Help -full Get-EventLog

Listing the last day that an entry was made in the Application Event Log

This is just a matter of using the -after parameter and subtracting 1 day from the actual date :

Get-EventLog   -ComputerName  Obiwan   -LogName  Application       -After ((Get-Date).adddays(-1))

Listing the System Event Log for the past two hours

To do this we also use the Get-Date methods, but use the -Before Parameter:

Get-EventLog   -ComputerName  Obiwan   -LogName  System     -Before ((Get-Date).addHours(-2))

Listing the Event Log between two time-periods

To do this, we can join the parameters -after and -before as well. Imagine if we need list all Securities event logs in the last day, but for the 3 hours ago from the current date/time:

Get-EventLog   -ComputerName  Obiwan   -LogName  Security   -After ((Get-Date).adddays(-1)     -Before ((Get-Date).addHours(-3))

This Table shows the parameters to filter by date/time:

Parameter Name

Description

After <Datetime>

Gets only the events that occur after the specified date and time. Enter a DateTime object, such as the one returnedby the Get-Date cmdlet.

Before <Datetime>

Gets only the events that occur before the specified date and time. Enter a DateTime object, such as the one returned by the Get-Date cmdlet.

Get-EventLog has a parameter to filter the event type according to whether they are errors, warnings, information or Audit States, There are also parameters to specify the source of the error and filter by the contents the message itself. This table describes these parameters:

Parameter Name

Description

EntryType <string[]>

Gets only events with the specified entry type. Valid values are Error, Information, FailureAudit, SuccessAudit, and Warning. The default is all events.

-Message <string>

Gets events that have the specified string in their messages. You can use this property to search for messages that contain certain words or phrases. Wildcards are permitted.

-Source <string[]>

Gets events that were written to the log by the specified sources. Wildcards are permitted.

Filtering the error log by the Error types

If we were looking at the Application Log for the ObiWan Server :

Get-EventLog   -ComputerName  Obiwan   -LogName  Application  -EntryType  Error

Reading errors from just one particular source

If you want to filter by the Source of the all event types. See that wildcards are allowed , so we can use for example , *sql* to filter all events from SQL Server.

Get-EventLog   -ComputerName  Obiwan   -EntryTpe  Error   -LogName  Application  -source  ‘*sql*’  

Reading all messages containing a specific string

We can filter by the contents of the message itself. Imagine if you want to filter the word ‘”started” in the message property :

Get-EventLog   -ComputerName  Obiwan   -LogName  Application  -Message  ‘*started* ‘

Selecting events according to a variety of conditions?

You can combine the selection of several properties. you may want to filter only the Event Log Application, type Error and source SQL Server in the last day :

Get-EventLog   -ComputerName  Obiwan   -LogName  Application   -EntryTpe  Error  -Source  ‘*sql*’  -After ((Get-Date).adddays(-1))

You may need to query not only the Error entry type, but also Warning. In the table above we see that the EntryType is a STRING[] type and this means that I can pass an array with ‘Error,Warning’ to the -EntryType parameter :

Get-EventLog   -ComputerName  Obiwan   -LogName  Application   -EntryTpe  ‘Error,Warning’   -Source  ‘*sql*’  -After ((Get-Date).adddays(-1))

This technique also applies to the -Source Parameter.

Selecting the event logs of a number of servers and instances?

Get-EventLog does not accept pipeline input,so I cannot use "ObiWan" | Get-EventLog.

However, , the-Computername parameter is a STRING[] type, so I can use it with an array. If you want to perform the above operation on the ObiWan and QuiGonJinn Servers it is just, as the -EntryType and -Source parameters, use the comma between the names of the servers:

Get-EventLog   -ComputerName @(‘Obiwan’  ,‘QuiGonJinn’)  -LogName  Application   -EntryType  Error  -Source  ‘*sql*’  -After ((Get-Date).adddays(-1))

Even better, using a txt file with the name of the servers, you also can do it with the Get-Content Cmdlet

Get-EventLog   -ComputerName (Get-Content  ‘c:tempMyServers’)  -LogName  Application   -EntryTpe  Error  -Source  ‘*sql*’  -After ((Get-Date).adddays(-1))

Reading the SQL Server Error Log

Not only does the SQL Server error log write error information but it also records some information about successful operations, such as recovery of a database; and it also includes informational messages, such as the TCP port that SQL Server is listening on. The SQL Server Error Log is simply a repository of events. All these events are logged in order to assist in troubleshooting a potential problem and also to provide key information about the sequence of steps leading up to the problem.

You can view the SQL Server Error Log using SQL Server Management Studio (SSMS). As it is a plain text file you can view it in any text editor From TSQL you can view the results of executing the xp_readerrorlog extended stored procedure. By default, the error log is stored at ...
Program FilesMicrosoft SQL ServerMSSQL.nMSSQLLOGERRORLOG.

The current file is named ERRORLOG, and has no extension. The previous files will be named ErrorLog.1, ErrorLog.2…etc. and the SQL Server retains backups of the previous six logs. Figure 1 shows a view for the SQL Server Error Log by the SSMS log viewer.

1921-clip_image002-500x284.png

Figure 2- SQL Server Error Log in SQL Server Management Studio

The SSMS user interface works when the SQL Server instance is online, but even works with offline instances in SQL Server 2012 or 2014

The advantage of using PowerShell to read the SQL Server Error Log is that you can filter only the errors and format the output for later reference, for example, writing it to a CSV file or storing it in a SQL Server Table. We’ll use this technique in some of our DBA checklists in a subsequent article.

Accessing SQL Error logs in Online SQL Server Instances

When the SQL Server Instance is online, we can use the SQLPSX Get-SqlErrorLog function to read the Error Log. This is part of SQLPSX, but for your convenience I have a stand-alone version that doesn’t need SQLPSX installed. Let’s start by using the Get-Help cmdlet with the -full parameter to see how this function works:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

PS C:> Get-Help Get-SqlErrorLog -Full

NAME

    Get-SqlErrorLog

SYNOPSIS

    Returns the SQL Server Errorlog.

SYNTAX

    Get-SqlErrorLog [-sqlserver] <String[]> [[-lognumber] <Int32>] [<CommonParameters>]

DESCRIPTION

    The Get-SqlErrorLog function returns the SQL Server Errorlog.

PARAMETERS

    -sqlserver <String[]>

        Required?                    true

        Position?                    1

        Default value                

        Accept pipeline input?       true (ByValue, ByPropertyName)

        Accept wildcard characters?  

    -lognumber <Int32>

        Required?                    false

        Position?                    2

        Default value                

        Accept pipeline input?       false

        Accept wildcard characters?  

    <CommonParameters>

        This cmdlet supports the common parameters: Verbose, Debug,

        ErrorAction, ErrorVariable, WarningAction, WarningVariable,

        OutBuffer and OutVariable. For more information, type,

        «get-help about_commonparameters».

OUTPUTS

    System.Data.DataRow

        Get-SqlErrorLog returns an array of System.Data.DataRow.

     EXAMPLE 1

    C:PS>Get-SqlErrorLog «Z002sql2k8»

    This command returns the current SQL ErrorLog on the Z002sql2k8 server.

RELATED LINKS

    Get-SqlErrorLog

As we can see in the help text we’ve gotten via the Get-Help cmdlet, we can pass in the SQL Server Instance name and the number of the log file, and the default of 0 corresponds to the current log. The -sqlserver parameter is mandatory. The SQLPSX version doesn’t accept pipeline input, so if you use this version you need to use foreach loop statment or foreach-object cmdlet from an array of the SQL Server instance names . So to list all events in SQL Error Log in the SQL Server Instance R2D2, using either version of Get-SqlErrorLog, we can use the form:

Get-SqlErrorLog   -sqlserver  R2D2  

Or using foreach loop statment :

$Servers  =  Get-Content  c:testeServers.txt  ForEach ($Server in $servers) {

    Get-SqlErrorLog -sqlserver $Server

}

And foreach-object cmdlet

Get-Content c:testeServers.txt |

ForEach-Object {

    Get-SqlErrorLog -sqlserver $_

}

Note If you want performance, avoid the pipeline and the foreach-object cmdlet approach. Use the foreach loop statment. We will discuss this approach in later articles.

Figure 3 illustrates the output:

1921-clip_image003-635x302.png

Figure 3- Get-SQLErrorLog output

Because the event description is truncated to fit the screen in this format, we can improve the formatting by piping the output to the Format-List cmdlet, as shown in Figure 3:

Get-SqlErrorLog   -sqlserver  R2D2  |  Format-List

1921-clip_image004-635x269.png

Figure 4- Get-SQLErrorLog output piping to Format-List

In the same way, if we want to list the SQL Server Error Log events in log file number 2, we can just use the -lognumber parameter:

Get-SqlErrorLog   -sqlserver  <SQLInstanceName>   -lognumber  2

Accessing SQL Error logs in OffLine SQL Server Instances

SQL Server 2012 introduced a new feature that allows the Error Log to be read even if the instance is offline. SQL Server has two WMI providers, for Server Events and for Computer Management.Two new WMI classes have been added to the Management provider: the SqlErrorLogFile and SqlErrorLogEvent classes.

To access these two classes you need to connect to the …
RootMicrosoftSqlServerComputerManagement11
… WMI namespace. Unlike the WMI for Server Events that has a namespace to each instance, the provider for Computer Management covers all SQL Server instances on the machine. You will need to specify the correct instance within the WQL (WMI Query Language).

The account under which the script runs needs to have read permissions, locally or remotely, on the RootMicrosoftSqlServerComputerManagement11 WMI namespace. It also needs permission to access the folder that contains the SQL Server Error Log File.

The SqlErrorLogFile WMI class contains information about the log file itself as we can see in the table 7.1:

Property

Description

ArchiveNumber

The number of the file. Corresponds to the extension of the ERRORLOG file, as 0,1,2…etc.

InstanceName

SQL Server Instance Name

LastModified

Date and time the file was last modified, using WMI date/time format

LogFileSize

The size of the log file in bytes

Name

Name of the file, ERRORLOG.X

This WMI Class is interesting if you want to know about the physical log file, but this is not our focus. Because we want the event descriptions and information, we need to use the SqlErrorLogEvent WMI Class. Table 7.2 shows the properties from the SqlErrorLogEvent class:

Property

Description

FileName

Name of the SQL Server Error Log file, ERRORLOG.1,.2,.3..etc

InstanceName

SQL Server Instance Name

LogDate

Date and time the event was recorded in the LOG File. Using WMI date/dime format

Message

The event message

ProcessInfo

Source of the Event (SPID)

To access the SQL Server Error Log for the default SQL Server instance in the Server R2D2, let’s use the Get-WMIObject cmdlet:

Get-WmiObject   -computername  R2d2  -Class  «SqlErrorLogEvent»   -Namespace  «RootMicrosoftSqlServerComputerManagement11»  

Figure 5 display the cmdlet’s output :

1921-clip_image005-635x208.png

Figure 5- Get-WMIObject Output in the SQLErrorLogEvent Class

You’ll see that, as well as the error log date and message, we’re also being distracted by some irrelevant information. As we can see in Figure 4, there are some properties that start with “__”. They are called WMI System Properties and are in every WMI Class. Unfortunately the Get-WMIObject cmdlet does not provide a parameter to suppress them in the output. An alternative is to select the properties you want to show, piping the output from Get-WMIObject to the Select-Object cmdlet :

Get-WmiObject   -Class  «SqlErrorLogEvent»  -ComputerName  R2D2  -Namespace  «RootMicrosoftSqlServerComputerManagement11»  |

Select-object  Filename,InstanceName,  Logdate,Message,ProcessInfo  

But we still have a problem. The LogDate is incomprehensible. Unlike the Get-SQLErrorLog where the property LogDate is a System.DateTime type that uses the OS date/time format, the LogDate property in the WMI Class is a System.String and has its own format. You can read more about this in “Working with Dates and Times using WMI” at Microsoft Technet. Figure 6 illustrates this:

1921-clip_image006-635x217.png

Figure 6- Get-WMIObject Output using Select-Object and displaying the LogDate in a WMI format.

This means that we need to convert the WMI date/time format to the system date/time format. Fortunately WMI has a method to perform this operation called ConvertToDateTime. We can just use it in the Select-Object step:

Get-WmiObject  -Class «SqlErrorLogEvent»  -ComputerName R2D2 -Namespace «RootMicrosoftSqlServerComputerManagement11»|

Select-object FileName,

       InstanceName,

       @{Expression={$_.ConvertToDateTime($_.LogDate)};Label = ‘Logdate’},

       Message,

       Processinfo

Uhaa!!! Now we have a friendly-view format to the Logdate property, as the Figure 7 shows :

1921-clip_image007-635x162.png

Figure 7- Get-WMIObject Output using Select-Object and displaying the LogDate property in a user-friendly format

This has now given us a way of gathering information about a SQL Server instance even if SQL Server is offline. We have accessed the log remotely in PowerShell by using WMI. This greatly increases our chances of solving a problem server even if the instance is offline.

Applying Filters to the SQL Error Log

If SQL Server does not start, or users have problems logging in, then you really have to search for possible errors. It is a good practice to check for errors and warnings just to be proactive and look for problems before they happen.. So far we saw how to read the events in the SQL Error Log and the Windows Event Viewer, but, as a DBA, we are interested on filtering these events, to look for specific errors and warnings or events that happen at a specific time. This covers how we filter the log messages.

SQL Error Log in Online SQL Server Instances

Imagine the situation, where you have been informed that two hours ago the SQL Server ObiWan, part of a simple active/passive cluster, was refusing connections from an XPTO. The Server in question is extremely busy and therefore it is likely to be a bad idea to use a resource-intensive graphic-user interface to diagnose it. You need to find out about what may have happened by filtering the Error Log looking for any error messages from two hours ago. You need to urgently inform your boss of the problem once you have enough information to be able to give an estimate of how long it will take to fix the problem. But the use of SSMS is out of the question. In fact we not only have to read the Error Log from two hours ago, but the filter the information looking for specific errors, but let’s approach the problem in stages.

PowerShell can easily work with date/time intervals because we can directly use the Get-Date properties. We already covered the Get-Date cmdlet in article 2, but let’s take a look a bit more deeply on it.

As everything in PowerShell is an object with its properties and methods, Get-Date returns an instance of System.DateTime and this class has a method named Adddays. If you don’t know about this method, Figure 8 display the output of only the methods that start with “ADD” (using the PowerShell cmdlet Get-Member) because it is all the matters to us now.

1921-clip_image008-635x209.png

Figure 8 – Only the methods that start with “ADD” from the Get-Date cmdlet

In section 7.1, “Reading the SQL Server Error Log”, we saw that this function returns the properties SQLInstanceName ,LogDate, ProcessInfo and Text . To filter by date/time we will use the LogDate property and to list the SQL Error Log in the last five days we’ll just use the Where-Object cmdlet , filtering the LogDate property using Get-Date and a negative number of the days, in this case, -5 , in the Adddays method :

Get-SqlErrorLog -sqlserver ObiWan |

Where-object { $_.logdate -ge ((Get-Date).adddays(-5)) }

But if we want the events from the last 24 hours only? Just use the AddHours method:

Get-SqlErrorLog -sqlserver ObiWan |

Where-object { $_.logdate -ge ((Get-Date).adddhours(-24)) }

The process to filter for Errors is similar, but we will need to pipe to the Where-Object Cmdlet to filter the errors but in this case we will use the Text property to look for strings that signify an error:

Get-SqlErrorLog -sqlserver ObiWan |

Where-object { (     $_.text -like ‘*Error*’ `

                     -or $_.text -like «*Fail*»`

                     -or $_.text -like «*dump*»`

                     -or $_.text -like ‘*IO requests taking longer*’`

                     -or $_.text -like ‘*is full*’ `

                  ) -and ($_.text  -notlike ‘*found 0 errors*’)`

                     -and ($_.text  -notlike ‘*without errors*’)`

             }

You can see the difficulty that we’ve had to ‘code around’, can’t you? Although the Event Viewer has a property that specifies whether the event is an Error, Warning or Information message, the Get-SQLErrorLog does not return this information and the error messages are embedded within in the message itself. The warnings sometimes contain text which contains the word ‘error’ but which aren’t actually error events. We don’t want to see those. This means that we need to filter “Error” but exclude “found 0 errors “or “without errors” and include some messages that do not have the “error” inside it, but characterizes an error or warning, as “is full” or “IO request taking longer”

‘In this example the trick is to use the operators -or and -and to filter exactly what you need. We can, however, produce neater code by using a RegEx string.

PowerShell works very well with Regex and, generally speaking, most of the string comparisons can be turned to a Regex Expression. It isn’t easy to understand the Regex patterns, but the result is clear code, without a bunch of the -and/-or operators. The same filter conditions used in the example above can be rewritten as:

Get-SqlErrorLog  -sqlserver  ObiWan  |

Where-object {$_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’  -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’ }

We see how to filter by date/time and by errors/warnings separately, but most of the time we prefer to have both type of events together so we just put the two together. In the next example, we filter by errors/warnings in the last 24 hours on ObiWan SQL Server Instance, the code is:

Get-SqlErrorLog  -sqlserver  ObiWan  |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

     -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’  -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

Now if you are checking for errors on all the servers that you’re responsible for, you will want to perform the same process, but for more than one SQL Server Instance. In our case let’s do it to the servers ObiWan and QuiGonJinn. Remembering the Get-Help from Get-SQLErrorLog in the first section of this article we noticed that the parameter -sqlserver accepts pipeline input and it is a STRING [] type. This applies to the version attached to this article: I’ve already shown how to use the SQLPSX version. In the rest of these examples, I’ll be using the enhanced version which can be downloaded from the head of this article.

I can pass a list of the servers:

… by pipeline …

‘ObiWan’,  ‘QuiGonJinn’  | Get-SqlErrorLog   |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

                    -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

                     -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

… having a txt file with the servers and using the Get-Content Cmdlet by pipeline

Get-Content  c:tempServers.txt  |

Get-SqlErrorLog   |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

                     -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

                    -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

… or having a txt file with the servers and using the Get-Content Cmdlet by array in the -sqlserver parameter because it is a STRING[] , just type:

Get-SqlErrorLog  -sqlserver (Get-Content  c:tempServers.txt)   |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

                     -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

                     -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

You can also obtain the list of SQL Server instance names from rows in a database table. In this case, I’m using a database called SQLServerRepository with a table called tbl_SQLServerInstanceNames on SQL Server instance R2D2. The table structure is pretty simple, just one column called SQLServerInstanceName.

In this case, you first need to query this table to return the SQL Server instance names using the Invoke-SQLCMD2 function that is part of the SQLPSX toolkit, and pipe the information to the Get-SQLErrorLog cmdlet:

Invoke-Sqlcmd2   -ServerInstance  R2D2  -Query  «Select SQLServerInstanceName from tbl_SQLServerInstanceNames»  -Database  «SQLServerRepository»  |

Get-SqlErrorLog  -sqlserver (Get-Content  c:tempServers.txt)   |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

                      -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

                     -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

You will notice that the output is sorting by ascending date of the LogDate Property . But what if we want to display the messages in descending order? To do this, we can just pipe the Where-Object cmdlets output to the Sort-Object designating the LogDate property and using the -descending switch parameter :

Invoke-Sqlcmd2   -ServerInstance  R2D2  -Query  «Select SQLServerInstanceName from tbl_SQLServerInstanceNames»  -Database  «SQLServerRepository»  |

Get-SqlErrorLog  -sqlserver (Get-Content  c:tempServers.txt)   |

Where-object { (      $_.logdate  -ge ((Get-Date).addhours(-24)))`

            -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

           -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}|  Sort-Object  Logdate  -descending

Now, to return to our scenario where that SQL Server was refusing connections, we need to filter messages from two hours ago and search for some error that might give us a clue as to what the problem is. To be more accurate, we will filter the time to ten minutes before two hours, or 130 minutes. From your desktop, you type:

Get-SqlErrorLog  -sqlserver  ObiWan   |

Where-object { (      $_.logdate  -ge ((Get-Date).addminutes(-130)))`

                     -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’`

                     -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}

In the output you see some interesting messages. As we can see in the Figure 9, the date/time of the errors are suspiciously close together and they are close to the date/time you were informed that SQL Server starts to refuse connections. The Text property displays the exact date/time that SQL Server stops responding to connections (logon error) and it was after the Dump Error.

1921-clip_image009-635x181.png

Figure 9 Reading and Filtering the SQL Server Error Log to solve the connection refused problem

At this point the cause of the problem will become obvious just from reading the output, the question has been answered by a PowerShell one-liner.. The server ObiWan is part of a cluster and because of the Dump error, it experienced a failover. For the duration of the failover, where the mechanism stopped the SQL Server service in one node and started it in the other, the connections were refused. It is a normal behavior during a failover. Your job now is to research why the dump happened, but that task is out of the scope of this article.

I’ve described the bare bones here. In fact, the text message is truncated to fit the screen and as so you’d usually want to pipe the command line above to the Out-GridView Cmdlet to get a better way of inspecting the errors:

Get-SqlErrorLog  -sqlserver  ObiWan   |

Where-object { (      $_.logdate  -ge ((Get-Date).addminutes(-130)))`

                     -and  $_.Text  -match  ‘(Error|Fail|IO requests taking longer|is full)’  -and  $_.Text  -notmatch  ‘(without errors|found 0 errors)’}Where-object { (    $_.logdate  -ge ((Get-Date).addminutes(-130)))`

                      -and ($_.text   -notlike  ‘*found 0 errors*’)`

                      -and ($_.text   -notlike  ‘*without errors*’)`

                      -and (  $_.text  -like  ‘*Error*’ `

                      -or  $_.text  -like  «*Fail*»`

                      -or  $_.text  -like  «*dump*»`

                      -or  $_.text  -like  ‘*IO requests taking longer*’`

                       -or  $_.text  -like  ‘*is full*’) `

             }  |  Out-GridView

The text messages are easy to read as the Figure 10 shows:

1921-clip_image010-635x310.png

Figure 10 Using the Out-GridView Cmdlet to achieve more User- friendly view of the Error Log

The Out-GridView Cmdlet has a plus. It has the filter options. This means that you use it as well. The Figure 10 is also displaying these options.

The SQL Server Error Log is a repository of events, whether they are errors, warnings or simple information messages. To filter for errors we need to include and exclude some messages at the same line as we did in the conditions above. The message “is full” was added, but “Without Errors” was added to our exclusion list. This means that if there is a line with both expressions it will be discarded. You may want to add more expressions on that condition to filter your needs more accurately. At some point, your filter conditions could become a bit unmanageable because you could find yourself changing the filter whilst exploring errors in the log. You really need something a bit more simple than the code we’ve done above. Possibly the best answer to this is to use a Regex but hide the complexity. By using PowerShell’s feature of variable-substitution in a string, we can keep things simpler. you can create a variable to -Match and -NoMatch operators, add all the conditions that you want, and use this in the Where-Object. This way it is easier for you understand, remove and add new filters for messages whatever you want or need and the search conditions for the Where-Object Cmdlet are clearer to read. The code would look like:

$match  =  ‘(Error|Fail|IO requests taking longer|is full)’

$nomatch  =  ‘(without errors|found 0 errors)’

Get-SqlErrorLog  -sqlserver  ObiWan   |

where {  $_.Text  -match  $match  -and  $_.Text  -notmatch  $nomatch }

To add a new message to the match condition, for example “Warning” it is just put it at the end of the string:

$match  =  ‘(Error|Fail|IO requests taking longer|is full|warning)’

The same process is used for the -nomatch conditions.

SQL Error Log in Offline SQL Server Instances

Imagine it: You’re at your desk analyzing the new ‘Always On’ project and you notice a report that , for some reason, the Servers R2D2 and ObiWan stopped start to refuse connections. After you solve the problem and not stop the production, your action could to consolidate the Error Log of the two Servers in the last half hour in an excel spreadsheet, each server in separate worksheets so that you can analyze the events.

From your desktop you just type:

«R2D2»,«ObiWan» | ForEach-Object { #A

       Get-WmiObject -Class «SqlErrorLogEvent»  -ComputerName $_ -Namespace «RootMicrosoftSqlServerComputerManagement11»| #B

       Where-Object {$_.ConvertToDateTime($_.LogDate) -ge (Get-Date).addminutes(-30)} | #C

       select        InstanceName,

       @{Expression={$_.ConvertToDateTime($_.LogDate)};Label = ‘Logdate’},

       Message,

       Processinfo |

       Sort-Object LogDate -Descending | #D

       Export-Xls -Path «c:Log.xlsx» -AppendWorksheet -WorksheetName $_ #E

}

#A — Loop for R2D2 and ObiWan Servers

#B — Accessing the SQL Server Error Log WMI at the server in the current loop

#C — Selecting the properties to display and changing the Logdate property from WMI Date format to the OS date format.

As the Figure 11 illustrates, an excel spreadsheet called Log.xlsx is created with the Servers R2D2 and ObiWan split into worksheets, with the last half hour events in descending date/time order:

1921-clip_image011small.png

Figure 11 Consolidated Error Log from ObiWan and R2D2 servers

The same operation by SSMS would be to:

  • Create a CSV File to each Server
  • Turn the CSV into an Excel spreadsheet
  • Sort the date/time in descending order
  • Copy each Excel spreadsheet to a new one as a worksheet

This is a relatively complex task if compared to just two command lines of PowerShell

We already covered way that you can read the SQL Error Log when the instance is offline by using the WMI class SQLErrorLogEvent, which is part of the WMI Computer Management Provider, and Get-WMIObject Cmdlet. The process by which one would filter in this case is a bit different to date/time and to choose the SQL Server instance. First let’s see the date/time process.

In the section of this article on offline SQL Server Instances, we saw that the WMI Classes have their own date/time format and so we need to convert this format to have a friendly-view format, or the system format. To filter by date/time we need to do the same to the LogDate Property but now using the Where-Object Cmdlet. In the example below, we are filtering the last one day event messages:

Get-WmiObject  -Class «SqlErrorLogEvent»  -ComputerName R2D2 -Namespace «RootMicrosoftSqlServerComputerManagement11»|

Where-Object { $_.ConvertToDateTime($_.LogDate)  -ge ((Get-Date).adddays(-1))} |

select FileName,

       InstanceName,

       @{Expression={$_.ConvertToDateTime($_.LogDate)};Label = ‘Logdate’},

       Message,            

       Processinfo

We also noticed that the WMI Computer Management Provider, unlike WMI for Server Events, reports on all SQL Server instances in the Server. This means that, so far, we only read the Error Log from the default SQL Server Instance. The -computername parameter in the GET-WMIObject refers to the name of the Server, not the SQL Server Instance.

Now imagine that you have five SQL Server Instances in the Server ObiWan and you need to read the Error Log from the fourth instance called ObiWanINST4, which is, of course, offline. How to perform this operation? In this case, my friend, the Windows Query Language (WQL) is your best and only friend.

In order to read the Error Log in the Server R2D2, specifically the SQL Server Instance R2D2INST4, we first need to query the InstanceName Property ‘INST4’ and so we will use the -Query property :

$WQL = «Select * from SqlErrorLogEvent where InstanceName = ‘INST4′»

Get-WmiObject  -Query $WQL -ComputerName Obiwan -Namespace «RootMicrosoftSqlServerComputerManagement11»|

select Filename,

       InstanceName,

       @{Expression={$_.ConvertToDateTime($_.LogDate)};Label = ‘Logdate’},

       Processinfo,

       message

To filter errors, we can do the same process with the Get-SQLErrorLog cmdlet using Where-Object, or we can use the WQL as well. In this case we need to create the conditions in the WQL using the Message property:

$WQL  =  «Select * from SqlErrorLogEvent where (Message like ‘%Error%’ or Message like ‘%Fail%’ ) and (not message like ‘%Found 0 Errors%’) and (not message like ‘%without errors%’)»

And to query only the SQL Server Instance INST4, it is just a case of adding the condition in the WQL:

$WQL  =  «Select * from SqlErrorLogEvent where (Message like ‘%Error%’ or Message like ‘%Fail%’ ) and (not message like ‘%Found 0 Errors%’) and (not message like ‘%without errors%’)and (InstanceName = ‘INST4’)»

To sort by date, it is also the same process. Just pipe the Sort-Object by LogDate before Select-Object and after Get-WMIObject.

Summary

In this article we discuss how to read and effectively filter errors, warnings or any other type of event, using the SQL Server Error Log in an Online and Offline SQL Server Instance. We also discovered how to use the Event Viewer and its parameters to filter the events searching for possible issues in the System, Security and Applications Event Logs.

In the example code, we use the enhanced version of Get-SQLErrorLog that does not require the installation of SQLPSX, and which accepts both string arrays and pipleline input. The SQLPSX version can, however, be used for most examples and can be made to operate on several instances by means of the techniques described in the article. The Get-SQLErrorLog can be downloaded from the link below.

Let’s face it. We’re not living in a perfect world. I am not a perfect programmer,
and the same probably applies to you (otherwise you would be stinkin’ rich,
laying on the beach somewhere drinking a Bacardi and Coke, instead of reading
this article).

Because we aren’t perfect, we know we are going to run into errors in the
applications we write sooner or later. This article will explain how you
can catch these errors in your ASP.NET applications and log them to the
Windows Event log, so it’s easy to review them later. The article focuses
on ASP.NET, but the principle applies to other .NET applications as well.

Prerequisites

In this article, I am going to use Visual Studio .NET 2003, so it’s handy if you have that to your availability. Visual Studio .NET 2002 will do as well, and you can even follow along if you program in Notepad and use the command line compilers.

I’ll be using C# as the main language, but you could easily write the code in Visual Basic .NET.

Errors? What Errors? I compiled the application and it looks fine!

With the arrival of IDEs like Visual Studio .NET and smart compilers, you can catch quite a lot of errors at design time. Syntax errors, undeclared variables, unsafe casts etc can all be caught by the compiler. So, some people believe that if they compile without getting an error or a warning from the compiler, their application is good and bug-free. Unfortunately, this is not true. Even though the program is free of syntax errors, you can still run into design mistakes or run-time errors. Important files could be missing, required fields are not filled in correctly, people break the interface of a public Web Service you were using and so on. These are all examples of errors that you cannot handle at design-time. You must handle them at run-time.

If these errors occur, you have to be sure that you get detailed information about them. Your user should never be bothered with detailed error information about call stacks and other scary technical jargon. Instead, you should present them with a friendly error message that explains in plain English what went wrong and that you were notified about the error and are now working very hard to fix it. You could also offer them an alternative to continue browsing your site, by providing them a link back to your home page for example. To find out how you can configure your application, be sure to read this article. It explains how to modify the Web.Config file and your Global class to catch errors and send an e-mail to the administrator of the site, informing him or her about the error, accompanied by a detailed error message.

We Want More!

Sending an e-mail is good because it will alert the right people right after the error occurred. However, e-mails tend to get lost, or popped at the wrong computer so you can’t access them from your development machine, or the ones who received the e-mail aren’t responsible for fixing the error.

So what would be another logical place to log the error? Right on the server where the error occurred, in the Windows Event log. Writing to the event log used to be problematic, but the .NET Framework has a few handy classes available that make writing to the log a breeze. In the remainder of this article I will demonstrate how you can catch your errors and log them to the Windows Event Log.

First Things First

With early beta releases of the ASP.NET Framework, it was possible to create new Events Logs (besides Application, Security and System) and Event Sources at run-time. That was because ASP.NET was running under the System account which had full privileges, including writing to the registry, which is necessary to create Event Logs and Sources. However, with the final releases of the .NET Framework (1.0 and 1.1), this is no longer possible. One way to fix this problem is to give the required permissions to the ASPNET account. However, this is not very recommended. The ASPNET account is a least privileged account, and for security reasons, it’s best to leave it that way. Besides, you do not really need to create the logs at run-time, as they are not likely to change that often. So, it’s best to create the Event Log and Source at design or deployment time. The easiest way to do this is to use a Windows Forms application. It’s easy to run such an application with administrative privileges (using Run As for example), so you can easily bypass the security restrictions.

Before I show you how to create this application, let’s take a look first at what the Event Log and the Event Source are. If you know all about the Event Log and just want to know how to create your own, go ahead and skip to the section Creating Your Own Event Log.

The Event Log

The Event Logs on your computer are accessible through the Event Viewer which you can find under the Administrative Tools menu. On a normal installation, you have three default Event Logs: Application, Security and System. The Application log is used to record information from applications. If you take a look in the Application log, you’ll see logs from applications you’re probably familiar with, like Office installations, SQL Server, backup programs and so on.
The Security log is not used much by default; however you can enable thorough access logging so more details about user sign-ons, access checks etc are logged.
The System log is used to log events from Windows components, like driver or service failures.
If you look at these three logs, you probably think that the Application log is best for logging your custom errors. That’s true, but there is even a better solution: the custom Event Log. I’ll get to that in a bit.

The Event Source

The Event Source is usually the name of the application that logged the event, but this is not required. In this article, however, I’ll stick to this recommendation and use the full address of my Web site as the name of the Event Source. This allows me to easily distinguish errors I am interested in from errors from other sources. Feel free to choose a different naming convention, though.

The following screen shot shows you the Event Viewer. You can see the custom Event Log, My Websites, in the right-hand pane and you can see that the events are logged under the source Imar.Spaanjaars.com, the name of the Web site that generated the event.

The Event Viewer with the Custom Event Log visible
Figure 1 — The Event Viewer with the Custom Event Log visible

Creating Your Own Event Log

It’s remarkably simple to create a new Event Log. All you need to do is call the static CreateEventSource method of the EventLog class that lives in the System.Diagnostics namespace. So, fire up Visual Studio .NET to create the application that will create the Event Log and Source. (The source code for this article includes the full source of the application, in case you want to try it out directly.)

  1. Once you started Visual Studio, create a new Visual C# Windows Application. Name the application CreateEventLog.
  2. Drag two labels, two textboxes and a button from the Toolbox to the form and set their properties as follows:
    Control (Name) Text
    Label 1 lblEventLog Event Log
    Label 2 lblEventSource Event Source
    Textbox 1 txtEventLog  
    Textbox 2 txtEventSource  
    Button 1 btnCreateEventLog Create Event Log

    Arrange the controls so they end up as in the following screen shot:

    The Sample Application
    Figure 2 — The Sample Application

  3. Double click the button, and add the following code for the btnCreateEventLog_Click handler:
    private void btnCreateEventLog_Click(object sender, System.EventArgs e)
    {
      if (txtEventLog.Text.Length > 0 && txtEventSource.Text.Length > 0)
      {
        System.Diagnostics.EventLog.CreateEventSource(
    txtEventSource.Text, txtEventLog.Text);
    MessageBox.Show("Event Log and Source " +
    "created successfully.");
    } else { MessageBox.Show("Please fill in both the " +
    "Event Log and the Event Source.");
    }
    }

    This code will check if both required fields are filled in, and if they are create the requested Event Log and Event Source.

  4. Press F5 to start the application, type My Websites for the Event Log name and the URL of your site, like www.MySite.com as the Event Source and click the Create Event Log button.
  5. Open up the Event Viewer (close it first if you had it still open to refresh its contents) and you’ll see your new Event Log as in the first screen shot of this article.
    That was easy, wasn’t it?

Writing to the Event Log

Creating a new Event Log and Source is easy, but writing to it is about as easy.

  1. First of all, make sure you followed the steps from the article How do I Handle Errors and Send an Error Notification from ASP.NET Applications? Make sure you changed at least the Web.Config file so it includes the customErrors node with its mode set to On:
    <customErrors mode="On">
    	<error statusCode="500" redirect="/InternalError.htm"/>
    </customErrors>

    The InternalError.htm page is the page your users will see when an error occurs and generally contains a user-friendly explanation that an error has occurred.

  2. Next, open the Web application that you want to log errors for and open the Code Behind page for the Global class (Global.asax.cs, or Global.asax.vb if you’re using VB.NET).
  3. Add the following using statement to the top of the Code Behind page:
    using System.Diagnostics;

  4. Add the following code to the Application_Error event. (If you followed along with the previous article, and added code that e-mailed you the detailed error information, add the following code below the code already present for the event):
    protected void Application_Error(Object sender, EventArgs e)
    {
      // Log error to the Event Log
      Exception myError = null;
      if (HttpContext.Current.Server.GetLastError() != null)
      {
        string eventLog = "My Websites";
        string eventSource = "www.MySite.Com";
        string myErrorMessage = "";
    
        myError = Server.GetLastError();
    
    while (myError.InnerException != null) { myErrorMessage += "Messagern" + myError.Message.ToString() + "rnrn"; myErrorMessage += "Sourcern" + myError.Source + "rnrn"; myErrorMessage += "Target sitern" + myError.TargetSite.ToString() + "rnrn"; myErrorMessage += "Stack tracern" + myError.StackTrace + "rnrn"; myErrorMessage += "ToString()rnrn" + myError.ToString(); // Assign the next InnerException // to catch the details of that exception as well myError = myError.InnerException; } // Make sure the Eventlog Exists if(EventLog.SourceExists(eventSource)) { // Create an EventLog instance and assign its source. EventLog myLog = new EventLog(eventLog); myLog.Source = eventSource; // Write the error entry to the event log. myLog.WriteEntry("An error occurred in the Web application "
    + eventSource + "rnrn" + myErrorMessage, EventLogEntryType.Error);
    } }
    }

    Don’t forget to replace the parts in bold with the names you have chosen for your Event Log and Source.

  5. To test out the new logging feature of your site, add a new Web Form to your application and call it TestLogging.aspx.
  6. Open the Code Behind for the page, and add the following code that will explicitly generate an error to the Page_Load event:
    private void Page_Load(object sender, System.EventArgs e)
    {
      throw new Exception("An error occurred in the " +
    "Page_Load of the TestLogging page.");
    }

  7. Save the page, compile your application and then open TestLogging.aspx in your browser. Instead of the usual error page, you should see your custom error page you defined in step 1.
  8. Start the Event Viewer and open your custom Event Log. You should see a new event log entry with the details of the error that occurred. Double-click the event log entry to see the dirty details of your error:

    The Event Properties dialog displays the detailed error message.
    Figure 3 — The Event Properties Dialog Displays the Detailed Error Nessage.

For this little example, I explicitly threw a new error using the throw keyword. However, the logging mechanism I showed you will catch all errors that may occur in your code, including any unhandled exceptions. This will help tremendously in pinpointing and fixing the problem.

Notice that the code in the Application_Errorevent uses a loop to retrieve information about the InnerException. If you don’t use the loop, you’ll end up with a rather meaningless «System.Web.HttpUnhandledException» message, without all the nitty gritty details about your error.

Also, sometimes explicitly nested errors are created, by passing in a Exception (the Inner Exception) to the constructor of another Exception (the Outer Exception) before the Outer Exception is thrown. This allows programmers to add useful information about the error, while leaving the original error and message in tact.

Instead of looping through the exceptions, you can also call GetBaseException() to get at the inner most exception directly:

myError = Server.GetLastError().GetBaseException();
	
myErrorMessage += "Messagern" +
	myError.Message.ToString() + "rnrn";
myErrorMessage += "Sourcern" +
	myError.Source + "rnrn";
myErrorMessage += "Target sitern" +
	myError.TargetSite.ToString() + "rnrn";
myErrorMessage += "Stack tracern" +
	myError.StackTrace + "rnrn";
myErrorMessage += "ToString()rnrn" +
	myError.ToString();  

What’s Next

The purpose of this article is to show you the general idea of logging to the Event Log. It’s by no means a complete solution (although you can use the code directly to enable logging in relatively small applications). Here are some ideas to extend the concepts that were introduced in this article:

  • Create a class ExceptionLogger with a few overloaded methods like LogError. Have the methods accept various flavors of exceptions, exception messages and EventLogEntryTypes (Error, Warning, Information and so on) and log the errors to the Event log. This allows you to log errors or other events to the Event log from within a Catch block, without interrupting the page flow and relying on the global exception handler.
  • Add backup code that logs the errors to a text file or sends out an e-mail in case the Event Log or Source does not exist. This shouldn’t happen because you created the Logs and Source yourself at installation time, but, hey, it’s not a perfect world.
  • Create different Event Sources for your application. This allows you to make a distinction between different kind of event log entries so it’s easier to locate the important ones.

There is Even More

Much of what I have introduced in this article, has already been taken care of by Microsoft. In April 2002, Microsoft released the Exception Management Application Block for .NET, a complete solution including full source code for publishing your errors to different logs, including the Event Log, SQL Server and a custom log that you define (an XML / text file or a pager or mobile phone for example).

The Application Block may be a bit overwhelming at first, and difficult to install and configure if you don’t read the documentation carefully. It’s not always necessary or applicable to use the Exception Management Application Block. For smaller sites or applications, the logging I demonstrated in this article should be more than enough.

Additionally, you can take a look at ELMAH (Error Logging Modules & Handlers), an open source project that allows you to send exception messages by e-mail or store them in a database. It’s easy to setup and can also be implemented in existing solutions without the need to recompile anything. You can check out the project on its project site.

References

  • Exception Management Application Block for .NET
  • System.Diagnostics Namespace
  • EventLog Class
  • EventLogEntryTypes Enumeration
  • How do I Handle Errors and Send an Error Notification from ASP.NET Applications?
  • Error Logging Modules & Handlers

Download Files

Source Code for this Article (Includes the Windows Application and two test web sites voor ASP.NET 1.x and ASP.NET 2.0)

Понравилась статья? Поделить с друзьями:
  • Event tracker fatal error
  • Event tracing fatal error windows 10 как исправить
  • Event id 4776 error code 0xc0000064
  • Escape from tarkov как изменить персонажа
  • Event id 1001 windows error reporting