Troubleshooting


Troubleshooting services is different from troubleshooting normal applications. This section covers troubleshooting topics such as:

  • The problems of interactive services

  • Event logging

  • Performance monitoring

The best way to start building a service is to create an assembly with the functionality you want and a test client, before the service is actually created. Here you can do normal debugging and error handling. As soon as the application is running, you can build a service by using this assembly. Of course, there might still be problems with the service:

  • Don't display errors in a message box from the service (except for interactive services that are running on the client system). Instead, use the event logging service to write errors to the event log. Of course, you can display a message box to inform the user about errors in the client application that uses the service.

  • The service cannot be started from within a debugger, but a debugger can be attached to the running service process. Open the solution with the source code of the service and set breakpoints. From the Visual Studio Debug menu, select Processes and attach the running process of the service.

  • The Performance Monitor can be used to monitor the activity of services. You can add your own performance objects to the service. This can add some useful information for debugging. For example, with the Quote service, you could set up an object to give the total number of quotes returned, the time it takes to initialize, and so on.

Interactive Services

If an interactive service runs with a logged-on user it can be helpful to display message boxes to the user. If the service should run on a server that is locked inside a computer room, the service should never display a message box. When you open a message box to wait for some user input, the user input probably won't happen for some days because nobody is looking at the server in the computer room; but it can get even worse than that: If the service isn't configured as an interactive service, the message box opens up on a different, hidden, window station. In this case, no one can answer that message box because it is hidden and the service is blocked.

Important

Never open dialog boxes for services running on a server system. Nobody will answer them.

In cases where you really want to interact with the user, an interactive service can be configured. Some examples of such interactive services are the Print Spooler that displays paper-out messages to the user, and the NetMeeting Remote Desktop Sharing service.

To configure an interactive service, you must set the option "Allow service to interact with desktop" in the Services configuration tool (see Figure 36-20). This changes the type of the service by adding the SERVICE_INTERACTIVE_PROCESS flag to the type.

image from book
Figure 36-20

Event Logging

Services can report errors and other information by adding events to the event log. A service class derived from ServiceBase automatically logs events when the AutoLog property is set to true. The ServiceBase class checks this property and writes a log entry at start, stop, pause, and continue requests.

In this section, you explore the following:

  • Error-logging architecture

  • Classes for event logging from the System.Diagnostics namespace

  • Adding event logging to services and to other application types

  • Creating an event-log listener with the EnableRaisingEvents property of the EventLog class

Figure 36-21 shows an example of a log entry from a service.

image from book
Figure 36-21

For custom event logging, you can use classes from the System.Diagnostics namespace.

Event Logging architecture

By default, the event log is stored in three log files: Application, Security, and System. Looking at the registry configuration of the event log service, you'll notice three entries under HKEY_LOCAL_ MACHINE\System\CurrentControlSet\Services\Eventlog with configurations pointing to the specific files. The System log file is used from the system and device drivers. Applications and services write to the Application log. The Security log is a read-only log for applications. The auditing feature of the operating system uses the Security log.

You can read these events by using the administrative tool Event Viewer. The Event Viewer can be started directly from the Server Explorer of Visual Studio by right-clicking on the Event Logs item and selecting the Launch Event Viewer entry from the context menu. The Event Viewer is shown in Figure 36-22.

image from book
Figure 36-22

In the event log, you can see this information:

  • The type can be Information, Warning, or Error. Information is an infrequent successful operation, Warning a problem that's not immediately significant, and Error a major problem. Additional types are FailureAudit and SuccessAudit, but these types are used only for the Security log.

  • Date and Time show the time when the event occurred.

  • The Source is the name of the software that logs the event. The source for the Application log is configured in:

    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ Eventlog\Application\[ApplicationName]

  • Below this key the value EventMessageFile is configured to point to a resource DLL that holds error messages.

  • A Category can be defined so that event logs can be filtered when using the Event Viewer.

    Categories can be defined by an event source.

  • The Event identifier specifies a particular event message.

Event Logging classes

The System.Diagnostics namespace has some classes for event logging:

  • With the EventLog class you can read and write entries in the event log, and establish applications as event sources.

  • The EventLogEntry class represents a single entry in the event log. With the EventLogEntryCollection you can iterate through EventLogEntry items.

  • The EventLogInstaller class is the installer for an EventLog component. EventLogInstaller calls EventLog.CreateEventSource() to create an event source.

  • With the help of the EventLogTraceListener, traces can be written to the event log. This class implements the abstract class TraceListener.

Adding event logging

If the AutoLog property of the ServiceBase class is set to true, event logging is automatically turned on. The ServiceBase class logs an informational event at startup, stop, pause, and continue requests of the service. In the ServiceInstaller class an EventLogInstaller instance is created so that an event log source is configured. This event log source has the same name as the service. If you want to write events you can use the WriteEntry() method of the EventLog class. The Source property was already set in the ServiceBase class:

 eventLog.WriteEntry("event log message"); 

This method logs an informational event. If warning or error events should be created, an overloaded method of WriteEvent() can be used to specify the type:

 eventLog.WriteEntry("event log message", EventLogEntryType.Warning);  eventLog.WriteEntry("event log message", EventLogEntryType.Error); 

Adding event logging to other application types

With services, the ServiceBase class automatically adds event-logging features. If you would like to use event logging within other application types, you can easily do that by using Visual Studio.

  • Use the toolbox to add an EventLog component to the Designer.

  • Set the Log property of the EventLog component to Application and the Source property to a name of your choice. This name is typically the name of the application that shows up in the Event Viewer.

  • Logs can now be written with the WriteEntry() method of the EventLog instance.

  • An installer can be added from the Add Installer context menu item of the EventLog component. This creates the ProjectInstaller class that configures the event source in the registry.

  • The application can now be registered with the installutil command. installutil calls the ProjectInstaller class and registers the event source.

If you do an xcopy-installation the last two steps are not really necessary. If the Source property for the EventLog instance is set, this source is automatically registered when an event log is written the first time, which is really easy to do. However, for a real application, you are better off adding the installer. With installutil /u the event log configuration is unregistered. If the application is just deleted, this registry key remains unless EventLog.DeleteEventSource() is called.

Adding event logging to the QuoteServer

The library QuoteServer used from the QuoteService currently doesn't have event logging included but this can easily be changed. To use the Visual Studio Designer to drag and drop the EventLog component to the class, you have to add designer support to the class.

Note

To add designer support to the class you have to derive the class from the base class System.ComponentModel.Component, and invoke the method InitializeComponent() inside the constructor of the class. The method InitializeComponent() that will be used from the Designer to set the properties of the components will be added automatically as soon as the first component is dropped onto the Designer surface, but you have to invoke this method yourself.

After you change the code of the QuoteServer class in the library QuoteServer with a derivation from the base class System.ComponentModel.Component, you can switch Visual Studio to the design view:

 public class QuoteServer : System.ComponentModel.Component {    //...    public QuoteServer() : this ("quotes.txt")    {    }    public QuoteServer(string filename) : this(filename, 7890)    {    }    public QuoteServer(string filename, int port)    {       this.filename = filename;       this.port = port; InitializeComponent();    }

After this change, you can drag and drop the EventLog component from the toolbox to the design view, where an instance of the EventLog class is created. Change the Log property of the object to Application and the Source property to QuoteService.

Then you can change the implementation of the method ListenerThread() in the class QuoteServer, so that an event log entry is written in case an exception is generated:

protected void ListenerThread() {    try    {       IPAddress ipAddress = Dns.Resolve("localhost").AddressList[0];       listener = new TcpListener(ipAddress, port);       listener.Start();       while (true)       {          Socket clientSocket = listener.AcceptSocket();          string message = GetRandomQuoteOfTheDay();          UnicodeEncoding encoder = new UnicodeEncoding();          byte[] buffer = encoder.GetBytes(message);          clientSocket.Send(buffer, buffer.Length, 0);          clientSocket.Close();       }    }    catch (SocketException ex)    { string message = "Quote Server failed in Listener: " + ex.Message; eventLog.WriteEntry(message, EventLogEntryType.Error);    } }

Trace

It's also possible that all your trace messages are redirected to the event log. You shouldn't really do this, because on a normal running system the event log gets overblown with trace messages, and the system administrator could miss the really important logs if this happens. Turning on trace messages to the event log can be useful when testing features for problematic services. Tracing is possible with debug as well as with release code.

To send trace messages to the event log you must create an EventLogTraceListener object and add it to the listener's list of the Trace class:

 EventLogTraceListener listener = new EventLogTraceListener(eventLog1);  Trace.Listeners.Add(listener); 

Now, all trace messages are sent to the event log:

 Trace.WriteLine("trace message"); 

Creating an event log listener

Next, you write an application that receives an event when a service encounters a problem. Create a simple Windows application that monitors the events of your Quote service. This Windows application consists of a list box and an Exit button only, as shown in Figure 36-23.

image from book
Figure 36-23

Add an EventLog component to the design view by dragging and dropping it from the toolbox. Set the Log property to Application, and the Source to the source of your service, QuoteService. The EventLog class also has a property, EnableRaisingEvents. The default value is false; setting it to true means that an event is generated each time this event occurs, and you can add an event handler for the EntryWritten event of the EventLog class. Add a handler with the name OnEntryWritten() to this event.

The OnEntryWritten() handler receives an EntryWrittenEventArgs object as argument, from which you can get the complete information about an event. With the Entry property an EventLogEntry object with information about the time, event source, type, category, and so on is returned:

 protected void OnEntryWritten (object sender,  System.Diagnostics.EntryWrittenEventArgs e) { DateTime time = e.Entry.TimeGenerated; string message = e.Entry.Message; listBoxEvents.Items.Add(time + " " + message); } 

The running application displays all events for the QuoteService as shown in Figure 36-24.

image from book
Figure 36-24

Performance Monitoring

Performance monitoring can be used to get information about the normal running of the service. Performance monitoring is a great tool that helps you understand the workload of the system, and observe changes and trends.

Microsoft Windows has a lot of performance objects, such as System, Memory, Objects, Process, Processor, Thread, Cache, and so on. Each of these objects has many counts to monitor. For example, with the Process object, the user time, handle count, page faults, thread count, and so on can be monitored for all processes, or for specific process instances. Some applications, such as SQL Server, also add application-specific objects.

For the quote service sample application it might be interesting to get information about the number of client requests, the size of the data sent over the wire, and so on.

Performance monitoring classes

The System.Diagnostics namespace provides these classes for performance monitoring:

  • PerformanceCounter can be used both to monitor counts and to write counts. New performance categories can also be created with this class.

  • PerformanceCounterCategory enables you to step through all existing categories as well as create new ones. You can programmatically get all the counters of a category.

  • PerformanceCounterInstaller is used for the installation of performance counters. The use is similar to the EventLogInstaller we discussed previously.

Performance Counter Builder

You can create a new performance counter category by selecting the performance counters in the Server Explorer and by selecting the menu entry Create New Category on the context menu. This launches the Performance Counter Builder (see Figure 36-25).

image from book
Figure 36-25

Set the name of the performance counter category to Quote Service. The following table shows all performance counters of the quote service.

Name

Description

Type

# of Bytes sent

Total # of bytes sent to the client

NumberOfItems32

# of Bytes sent/sec

# of bytes sent to the client in one second

RateOfCountsPerSecond32

# of Requests

Total # of requests

NumberOfItems32

# of Requests/sec

# of requests in one second

RateOfCountsPerSecond32

The Performance Counter Builder writes the configuration to the performance database. This can also be done dynamically by using the Create() method of the PerformanceCounterCategory class in the System.Diagnostics namespace. An installer for other systems can easily be added later using Visual Studio.

Adding PerformanceCounter components

Now you can add PerformanceCounter components from the toolbox. Instead of using the components from the toolbox category Components, you can directly drag and drop the previously created performance counts from the Server Explorer to the design view. This way the instances are configured automatically: the CategoryName property is set to "Quote Service Count" for all objects, and the CounterName property is set to one of the values available in the selected category. Because with this application the performance counts will not be read but written, you have to set the ReadOnly property to false.

Here is a part of the code generated into InitalizeComponent() by adding the PerformanceCounter components to the Designer and by setting the properties as indicated previously:

 private void InitializeComponent() { //... // performanceCounterRequestsPerSec //  this.performanceCounterRequestsPerSec.CategoryName =  "Quote Service Counts"; this.performanceCounterRequestsPerSec.CounterName = "# of Requests / sec"; this.performanceCounterRequestsPerSec.MachineName = "cnagel"; this.performanceCounterReqeustsPerSec.ReadOnly = false; //  // performanceCounterBytesSentTotal //  this.performanceCounterBytesSentTotal.CategoryName =  "Quote Service Counts"; this.performanceCounterBytesSentTotal.CounterName = "# of Bytes sent"; this.performanceCounterBytesSentTotal.MachineName = "cnagel"; this.performanceCounterBytesSentTotal.ReadOnly = false; //  // performanceCounterBytesSentPerSec //  this.performanceCounterBytesSentPerSec.CategoryName =  "Quote Service Counts"; this.performanceCounterBytesSentPerSec.CounterName =  "# of Bytes sent / sec"; this.performanceCounterBytesSentPerSec.MachineName = "cnagel"; this.performanceCounterBytesSentPerSec.ReadOnly = false; //  // performanceCounterRequestsTotal //  this.performanceCounterRequestsTotal.CategoryName =  "Quote Service Counts"; this.performanceCounterRequestsTotal.CounterName = "# of Requests"; this.performanceCounterRequestsTotal.MachineName = "cnagel"; this.performanceCoutnerRequestsTotal.ReadOnly = false; //... } 

For the calculation of the performance values, you have to add the fields requestsPerSec and bytesPerSec to the class QuoteServer:

public class QuoteServer : System.ComponentModel.Component { // Performance monitoring counts private int requestsPerSec; private int bytesPerSec; 

The performance counts that show the total values are incremented directly in the ListenerThread() method (shown in the following code) of the QuoteServer class. You can use PerformanceCounter.Increment() to count the number of total requests, and IncrementBy() to count the number of bytes sent.

For the performance counts that show the value by seconds, just the two variables, requestsPerSec and bytesPerSec, are updated in the ListenerThread() method:

protected void ListenerThread() {    try    {       listener = new TCPListener(port);       listener.Start();       while (true)       {          Socket clientSocket = listener.Accept();          string message = GetRandomQuoteOfTheDay();          UnicodeEncoding encoder = new UnicodeEncoding();          byte[] buffer = encoder.GetBytes(message);          clientSocket.Send(buffer, buffer.Length, 0);          clientSocket.Close(); performanceCounterRequestsTotal.Increment(); performanceCounterBytesSentTotal.IncrementBy(buffer.Length); requestsPerSec++; bytesPerSec += buffer.Length; } } catch (SocketException ex) { string message = "Quote Server failed in Listener: " + ex.Message; EventLog.WriteEntry("QuoteService", message); } } 

To show updated values every second, add a Timer component. Set the OnTimer() method to the Elapsed event of this component. The OnTimer() method is called once per second if you set the Interval property to 1000. In the implementation of this method, set the performance counts by using the RawValue property of the PerformanceCounter class:

 protected void OnTimer (object sender, System.Timers.ElapsedEventArgs e) { performanceCounterBytesSentPerSec.RawValue = bytesPerSec; performanceCounterRequestsPerSec.RawValue = requestsPerSec; bytesPerSec = 0; requestsPerSec = 0; } 

perfmon.exe

Now you can monitor the service. You can start the Performance tool by selecting Administrative Tools Performance. By pressing the + button in the toolbar, you can add performance counts. The Quote Service shows up as a performance object. All the counters that have been configured show up in the counter list as shown in Figure 36-26.

image from book
Figure 36-26

After you've added the counters to the performance monitor, you can see the actual values of the service over time (see Figure 36-27). Using this performance tool, you can also create log files to analyze the performance at a later time.

image from book
Figure 36-27




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net