ENTERPRISE

Microsoft Enterprise Library : Non-Formatted Trace Listeners (part 2) - Logging to a Database, Testing Logging Filter Status

9/12/2012 1:11:18 AM

4. Logging to a Database

One of the most common requirements for logging, after Windows Event Log and text files, is to store log entries in a database. The Logging block contains the database trace listener that makes this easy. You configure the database using a script provided with Enterprise Library, located in the \Blocks\Logging\Src\DatabaseTraceListener\Scripts folder of the source code.

The scripts assume that you will use the locally installed SQL Server Express database, but you can edit the CreateLoggingDb.cmd file to change the target to a different database server. The SQL script that the command file executes creates a database named Logging, and adds the required tables and stored procedures to it.

The project contains a preconfigured database file named Logging.mdf (located in the bin\Debug folder) that is auto-attached to your local SQL Server Express instance. You can connect to this database using Visual Studio Server Explorer to see the contents. The configuration of the database trace listener contains the Database Instance property, which is a reference to this database as configured in the settings section for the Data Access application block (see Figure 2).

Figure 2. Configuration of the Database trace listener


The database trace listener uses a text formatter to format the output, and so you can edit the template used to generate the log message to suit your requirements. You can also add extended properties to the log entry if you wish. In addition, as with all trace listeners, you can filter log entries based on their severity if you like.

The Log table in the database contains columns for only the commonly required values, such as the message, event ID, priority, severity, title, timestamp, machine and process details, and more. It also contains a column named FormattedMessage that contains the message generated by the text formatter.

4.1. Using the Database Trace Listener

The example, Sending log entries to a database, demonstrates the use of the database trace listener. The code is relatively simple, following the same style as the earlier example of creating a Dictionary of extended properties, and then using the Write method of the LogWriter to write two log entries. The first log entry is created by the LogWriter from the parameter values provided to the Write method. The second is generated in code as a new LogEntry instance by specifying the values for the constructor parameters. Also notice how easy it is to add additional information to a log entry using a simple Dictionary as the ExtendedProperties of the log entry.

// Check if logging is enabled before creating log entries.
if (defaultWriter.IsLoggingEnabled())
{
  // Create a Dictionary of extended properties
  Dictionary<string, object> exProperties = new Dictionary<string, object>();
  exProperties.Add("Extra Information", "Some Special Value");
  // Create a LogEntry using the constructor parameters.
  defaultWriter.Write("Log entry with category, priority, event ID, severity, "
                      + "title, and extended properties.", "Database",
                      5, 9008, TraceEventType.Warning,
                      "Logging Block Examples", exProperties);
  // Create a LogEntry using the constructor parameters.
  LogEntry entry = new LogEntry("LogEntry with category, priority, event ID, "
                                + "severity, title, and extended properties.",
                                "Database", 8, 9009, TraceEventType.Error,
                                "Logging Block Examples", exProperties);
  defaultWriter.Write(entry);
}
else
{
  Console.WriteLine("Logging is disabled in the configuration.");
}

					  

To see the two log messages created by this example, you can open the Logging.mdf database from the bin\Debug folder using Visual Studio Server Explorer. You will find that the FormattedMessage column of the second message contains the following. You can see the extended property information we added using a Dictionary at the end of the message.

Timestamp: 03/12/2009 17:14:02
Message: LogEntry with category, priority, event ID, severity, title, and extended
properties.
Category: Database
Priority: 8
EventId: 9009
Severity: Error
Title: Logging Block Examples
Activity ID: 00000000-0000-0000-0000-000000000000
Machine: BIGFOOT
App Domain: LoggingExample.vshost.exe
ProcessId: 5860
Process Name: E:\Logging\Logging\bin\Debug\LoggingExample.vshost.exe
Thread Name:
Win32 ThreadId:3208
Extended Properties: Extra Information - Some Special Value

					  

Note that you cannot simply delete logged information due to the references between the Log and CategoryLog tables. However, the database contains a stored procedure named ClearLogs that you can execute to remove all log entries.


Note:

The connection string for the database we provide with this example is:

Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Logging. mdf;Integrated Security=True;User Instance=True

If you have configured a different database using the scripts provided with Enterprise Library, you may find that you get an error when you run this example. It is likely to be that you have an invalid connection string in your App.config file for your database. In addition, use the Services applet in your Administrative Tools folder to check that the SQL Server (SQLEXPRESS) database service (the service is named MSSQL$SQLEXPRESS) is running.


5. Testing Logging Filter Status

As you've seen in earlier examples, the Logging block allows you to check if logging is enabled before you create and write a log entry. You can avoid the additional load that this places on your application if logging is not enabled. However, even when logging is enabled, there is no guarantee that a specific log entry will be written to the target log store. For example, it may be blocked by a priority filter if the message priority is below a specified level, or it may belong only to one or more categories where the relevant category filter(s) have logging disabled (a common scenario in the case of logging code specifically designed only for debugging use).

The example, Checking filter status and adding context information to the log entry, demonstrates how you can check if a specific log entry will be written to its target before you actually call the Write method. After checking that logging is not globally disabled, the example creates two LogEntry instances with different categories and priorities. It passes each in turn to another method named ShowDetailsAndAddExtraInfo. The following is the code that creates the LogEntry instances.

// Check if logging is enabled before creating log entries.
if (defaultWriter.IsLoggingEnabled())
{
  // Create a new LogEntry with two categories and priority 3.
  string[] logCategories = new string[] {"General", "DiskFiles"};
  LogEntry entry1 = new LogEntry("LogEntry with categories 'General' and "
                                 + "'DiskFiles' and Priority 3.", logCategories,
                                 3, 9009, TraceEventType.Error,
                                 "Logging Block Examples", null);
  ShowDetailsAndAddExtraInfo(entry1);
  // Create a new LogEntry with one category and priority 1.
  logCategories = new string[] { "BlockedByFilter" };
  LogEntry entry2 = new LogEntry("LogEntry with category 'BlockedByFilter' and "
                                 + "Priority 1.", logCategories, 1, 9010,
                                 TraceEventType.Information,
                                 "Logging Block Examples", null);
  ShowDetailsAndAddExtraInfo(entry2);
}
else
{
  Console.WriteLine("Logging is disabled in the configuration.");
}

					  

The ShowDetailsAndAddExtraInfo method takes a LogEntry instance and does two different things. Firstly, it shows how you can obtain information about the way that the Logging block will handle the log entry. This may be useful in advanced scenarios where you need to be able to programmatically determine if a specific log entry was detected by a specific trace source, or will be written to a specific target. Secondly, it demonstrates how you can check if specific filters, or all filters, will block a log entry from being written to its target.

5.1. Obtaining Information about Trace Sources and Trace Listeners

The first section of the ShowDetailsAndAddExtraInfo method iterates through the collection of trace sources (LogSource instances) exposed by the GetMatchingTraceSources method of the LogWriter class. Each LogSource instance exposes a Listeners collection that contains information about the listeners (which specify the targets to which the log entry will be sent).

void ShowDetailsAndAddExtraInfo(LogEntry entry)
{
  // Display information about the Trace Sources and Listeners for this LogEntry.
  IEnumerable<LogSource> sources = defaultWriter.GetMatchingTraceSources(entry);
  foreach (LogSource source in sources)
  {
    Console.WriteLine("Log Source name: '{0}'", source.Name);
    foreach (TraceListener listener in source.Listeners)
    {
      Console.WriteLine(" - Listener name: '{0}'", listener.Name);
    }
  }
  ...

					  

5.2. Checking if Filters Will Block a Log Entry

Next, the ShowDetailsAndAddExtraInfo method checks if any filters will block the current log entry. There are two ways you can do this. You can query each filter type in turn, or just a specific filter type, by using the GetFilter method of the LogWriter class to get a reference to that type of filter. Then you can check if this filter is enabled, and also use the ShouldLog method (to which you pass the list of categories for the log entry) to see if logging will succeed.

The following code shows this approach. It also shows the simpler approach that you can use if you are not interested in the result for a specific filter type. The LogWriter class also exposes the ShouldLog method, which indicates if any filters will block this entry.

 ...
 // Check if any filters will block this LogEntry.
 // This approach allows you to check for specific types of filter.
 // If there are no filters of the specified type configured, the GetFilter
 // method returns null, so check this before calling the ShouldLog method.
 CategoryFilter catFilter = defaultWriter.GetFilter<CategoryFilter>();
 if (null == catFilter || catFilter.ShouldLog(entry.Categories))
 {
   Console.WriteLine("Category Filter(s) will not block this LogEntry.");
 }
 else
 {
   Console.WriteLine("A Category Filter will block this LogEntry.");
 }
 PriorityFilter priFilter = defaultWriter.GetFilter<PriorityFilter>();
 if (null == priFilter || priFilter.ShouldLog(entry.Priority))
 {
   Console.WriteLine("Priority Filter(s) will not block this LogEntry.");
 }
 else
 {
   Console.WriteLine("A Priority Filter will block this LogEntry.");
 }
 // Alternatively, a simple approach can be used to check for any type of filter
 if (defaultWriter.ShouldLog(entry))
 {
   Console.WriteLine("This LogEntry will not be blocked by config settings.");
   ....
   // Add context information to log entries after checking that the log entry
   // will not be blocked due to configuration settings. See the following
   // section 'Adding Additional Context Information' for details.
   ....
 }
 else
 {
   Console.WriteLine("This LogEntry will be blocked by configuration settings.");
 }
}

					  

After you determine that logging will succeed, you can add extra context information and write the log entry. You'll see the code to achieve this shortly. In the meantime, this is the output generated by the example. You can see that it contains details of the log (trace) sources and listeners for each of the two log entries created by the earlier code, and the result of checking if any category filters will block each log entry.

Created a LogEntry with categories 'General' and 'DiskFiles'.
Log Source name: 'General'
 - Listener name: 'Formatted EventLog TraceListener'
Log Source name: 'DiskFiles'
 - Listener name: 'FlatFile TraceListener'
 - Listener name: 'XML Trace Listener'
Category Filter(s) will not block this LogEntry.
Priority Filter(s) will not block this LogEntry.
This LogEntry will not be blocked due to configuration settings.
...
Created a LogEntry with category 'BlockedByFilter', and Priority 1.
Log Source name: 'BlockedByFilter'
 - Listener name: 'Formatted EventLog TraceListener'
A Category Filter will block this LogEntry.
A Priority Filter will block this LogEntry.
This LogEntry will be blocked due to configuration settings.
Other  
  •  Introducing Windows Presentation Foundation and XAML : Transforming Markup into a .NET Assembly
  •  Introducing Windows Presentation Foundation and XAML : Building a WPF Application using Only XAML
  •  Intel : We For One Welcome Our Apple Overlords
  •  Azure Table Storage (part 2) - Accessing Table Storage, Choosing a PartitionKey, Exception handling
  •  Azure Table Storage (part 1) - Table Storage versus database tables
  •  Cheetah3D 6 : Britain's next top modeler
  •  System Center Configuration Manager 2007 : Developing the Solution Architecture (part 5) - Site Design,Client Architecture,Multilanguage Scenarios
  •  System Center Configuration Manager 2007 : Developing the Solution Architecture (part 4) - Capacity Planning,Site Boundaries,Roaming
  •  System Center Configuration Manager 2007 : Developing the Solution Architecture (part 3) - Developing the Server Architecture
  •  System Center Configuration Manager 2007 : Developing the Solution Architecture (part 2) - Configuration Manager 2007 Roles
  •  System Center Configuration Manager 2007 : Developing the Solution Architecture (part 1) - Developing the Network Infrastructure
  •  System Center Configuration Manager 2007 : Operating System Deployment Planning, Out of Band Management Planning
  •  Visual Studio 2010 IDE : Customizing Visual Studio 2010
  •  Visual Studio 2010 IDE : Exporting Templates
  •  System Center Configuration Manager 2007 : Certificate Requirements Planning, Windows Server 2008 Planning
  •  System Center Configuration Manager 2007 : Planning for Internet-Based Clients
  •  Active Directory Domain Services 2008 : Automatically Populate a Migration Table from a Group Policy Object
  •  Active Directory Domain Services 2008 : Create a Migration Table
  •  Microsoft Content Management Server : Developing Custom Properties for the Web Part
  •  Microsoft Content Management Server : Building SharePoint Web Parts - Creating the Web Part, Defining Custom Properties for the Web Part
  •  
    Most View
    Managing Alarms - Take Control Of Your Device’s Alarm
    Arctic Cooling Accelero Hybrid GTX 680
    Sony Alpha A58 Digital SLT Camera (Part 2)
    Graphics Card Shootout - Budget FPS (Part 2)
    Sony VAIO Duo 11 - A Swath Of New Hybrid Tablet-Laptop
    Best Apps For Your SmartPhones – December 2012 (Part 3)
    Kingston Wi - Drive 128GB: Simple To Get Started
    Gigabyte GA-Z77-D3H Mainboard - Not So Complicated LGA 115 Mainboard (Part 4)
    A Quick View Of The Industry: Big Data
    OpenGL on OS X : OpenGL with Cocoa (part 2)
    Top 10
    Windows Management and Maintenance : The Windows 7 Control Panel (part 11) - Region and Language, System
    Windows Management and Maintenance : The Windows 7 Control Panel (part 10) - Programs and Features
    Windows Management and Maintenance : The Windows 7 Control Panel (part 9) - Notification Area Icons, Performance Information and Tools
    Windows Management and Maintenance : The Windows 7 Control Panel (part 8) - Fonts
    Windows Management and Maintenance : The Windows 7 Control Panel (part 7) - Ease of Access Center
    Windows Management and Maintenance : The Windows 7 Control Panel (part 6) - Devices and Printers
    Windows Management and Maintenance : The Windows 7 Control Panel (part 5) - AutoPlay
    Windows Management and Maintenance : The Windows 7 Control Panel (part 4) - AutoPlay
    Windows Management and Maintenance : The Windows 7 Control Panel (part 3) - Action Center
    Windows Management and Maintenance : The Windows 7 Control Panel (part 2)