Logging with custom diagnostic categories from sandbox

Nov 2, 2011 at 1:45 AM
Edited Nov 2, 2011 at 1:52 AM

I'm trying to go through a few proof-of-concept features using SPG, and I'm having trouble getting logging from the sandbox to work.  I've got a feature with an event receiver configured like this:

 

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;

using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Security;
using Microsoft.Practices.SharePoint.Common.Logging;

using Wingtip.Global.Logging;

namespace LeadTracker.Features.ProductDevelopment
{
	[Guid("ae0da7c1-798b-459d-b752-55e413d95700")]
	public class ProductDevelopmentEventReceiver : SPFeatureReceiver
	{
		private IServiceLocator serviceLocator;
		private ILogger logger;

		public ProductDevelopmentEventReceiver()
		{
			serviceLocator = SharePointServiceLocator.GetCurrent();
			logger = serviceLocator.GetInstance<ILogger>();
		}

		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			logger.TraceToDeveloper("FeatureActivated called", 1026, Areas.Wingtip.ProductDevelopment);
		}

		public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
		{
			logger.TraceToDeveloper("FeatureDeactivating called", 1026, Areas.Wingtip.ProductDevelopment);
		}
	}
}

 

I installed the configuration and logging proxy solutions in the latest release, so if I comment out the <tt>logger.TraceToDeveloper</tt> lines, this all work just fine.  I have a farm solution called <tt>Wingtip.Global</tt> (I'm using one of the examples in Inside SharePoint 2010) which has its own feature receiver that configures custom diagnostic areas and categories, which I can verify in the logging configuration page on the central admin site.  However, when I uncomment the <tt>logger.TraceToDeveloper</tt> lines, I get the following errors (emphasis mine), which go on and on in the ULS logs:

Feature Activation: Threw an exception, attempting to roll back.  Feature 'Wingtip.Sandboxed_ProductDevelopment' (ID: 'bff187be-dcc4-4268-b3c6-cb8339d7e3b4').  Exception: Microsoft.SharePoint.UserCode.SPUserCodeSolutionExecutionFailedException: Unhandled exception was thrown by the sandboxed code wrapper's Execute method in the partial trust app domain: An unexpected error has occurred. ---> Microsoft.SharePoint.UserCode.SPUserCodeSolutionProxiedException: One or more error occurred while writing messages into the log.\r\nThe error while writing to the EventLog was:An exception has occurred.   ExceptionType: 'LoggingException'   ExceptionMessage: 'The attempt to trace from the sandbox failed.'   StackTrace: '   at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToTraceSandbox(String message, Int32 eventId, Nullable`1 severity, String category)       at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToDeveloperTrace(String message, Int32 eventId, String category)'   Source: 'Microsoft.Practices.SharePoint.Common'   TargetSite: 'Void WriteToTraceSandbox(System.String, Int32, System.Nullable`1[Microsoft.Practices.SharePoint.Common.Logging.SandboxTraceSeverity], System.String)'   ------------------------------------------------------------   Inner exception:   ------------------------------------------------------------    ExceptionType: 'LoggingException'    ExceptionMessage: 'One or more error occurred while writing messages into the log.\r\nThe error while writing to the EventLog was:An exception has occurred.      ExceptionType: 'LoggingException'      ExceptionMessage: 'The Category '{0}' was not found in the diagnostic categories collections.'      StackTrace: '   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.ParseCategoryPath(String categoryPath)          at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.FindCategory(String categoryPath)          at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.GetCategory(String categoryName)          at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.LogTrace(String message, Int32 eventId, String category)          at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToDeveloperTrace(String message, Int32 eventId, String category)'      Source: 'Microsoft.Practices.SharePoint.Common'     

TargetSite: 'System.String[] ParseCategoryPath(System.String)'     \r\n Orginal logged message was: An error occurred while writing tot the Trace Log, trace message:Sandbox Trace,Missing Site Context, Message:'FeatureActivated called''    StackTrace: '   at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToDeveloperTrace(String message, Int32 eventId, String category)        at Microsoft.Practices.SharePoint.Common.Logging.BaseLogger.TraceToDeveloper(String message, Int32 eventId, String category)        at Microsoft.Practices.SharePoint.Common.Logging.LoggerProxy.TracingOperation.Execute(SPProxyOperationArgs args)'    Source: 'Microsoft.Practices.SharePoint.Common'    TargetSite: ''    Additional Data:     'OriginalMessage' : 'An error occurred while writing tot the ...

 

I did check at runtime to make sure that the strings (<tt>Areas.Wingtip.*</tt>) were valid and match what the diagnostic logging configuration page shows.  Why would it fail like this?

Coordinator
Nov 2, 2011 at 12:13 PM

You should be able to use custom categories.  I think there may be an inconsistency somewhere that is slipping through even though it looks like you have checked that.

I don't see here where you are registering the area/category - just wondering where you did it, and are you sure that the two are consistent?  What is the value you registered?

I don't think this has to do with the sandbox, from your stack trace it looks like the proxy is being called correctly.  Have you tried logging to the area/category outside of the sandbox?  If that works then I'll be a lot more perplexed...

What is the value of Areas.Wingtip.ProductDevelopment?

Thanks,
Chris

Nov 2, 2011 at 1:44 PM
Edited Nov 2, 2011 at 1:45 PM

The rest of the code is in my <tt>Wingtip.Global</tt> project, which contains a few classes to help configure areas and a feature called LoggingConfiguration with an event receiver to add the areas to the collection.

First, I have a couple of attributes:

 

using System;
using Microsoft.SharePoint.Administration

namespace Wingtip.Global.Logging
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
	sealed class AreaAttribute : Attribute
	{
		public string DisplayName { get; set; }
	}

	[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	sealed class CategoryAttribute : Attribute
	{
		public EventSeverity DefaultEventSeverity { get; set; }
		public TraceSeverity DefaultTraceSeverity { get; set; }
	}
}

 

I use these to set up static classes as areas/categories:

 

using Microsoft.SharePoint.Administration;

namespace Wingtip.Global.Logging
{
	public static class Areas
	{		
		[Area(DisplayName = "Wingtip")]
		public static class Wingtip
		{
			[Category(DefaultEventSeverity = EventSeverity.Information, DefaultTraceSeverity = TraceSeverity.Verbose)]
			public static string ProductDevelopment
			{
				get { return "Product Development"; }
			}
		}
	}
}

 

And finally, my event receiver for the LoggingConfiguration feature (which is deployed in a farm solution, as described in the OP):

 

using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;

using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Wingtip.Global.Logging;

namespace Wingtip.Global.Features.LoggingConfiguration
{
	[Guid("5f49c1dd-9204-4c55-9802-ccdc9061f42b")]
	public class LoggingConfigurationEventReceiver : SPFeatureReceiver
	{				
		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in typeof(Areas).GetNestedTypes().Where(t => t.GetCustomAttributes(typeof(AreaAttribute), false).Any()))
			{
				AreaAttribute areaAttr = area.GetCustomAttributes(typeof(AreaAttribute), false).First() as AreaAttribute;
				if (null == areaCollection[areaAttr.DisplayName])
				{
					areaCollection.Add(BuildArea(area));
				}

			areaCollection.SaveConfiguration();
		}

		private DiagnosticsArea BuildArea(Type areaType)
		{
			var cats = new DiagnosticsCategoryCollection();
			foreach (var category in areaType.GetProperties().Where(p => p.GetCustomAttributes(typeof(CategoryAttribute), false).Any()))
			{
				var catAttr = category.GetCustomAttributes(typeof(CategoryAttribute), false).First() as CategoryAttribute;
				cats.Add(new DiagnosticsCategory(category.GetValue(null, null) as string, 
					catAttr.DefaultEventSeverity, catAttr.DefaultTraceSeverity));
			}

			AreaAttribute areaAttr = areaType.GetCustomAttributes(typeof(AreaAttribute), false).First() as AreaAttribute;
			return new DiagnosticsArea(areaAttr.DisplayName, cats);
		}
	}
}

 

Note that I don't have a definition for FeatureDeactivating.  I had an implementation, but then I switched to use the static classes and reflection to build up the areas and I just haven't updated that part of the receiver yet.

The result of activating this feature on the farm (powershell):

 

PS C:\Users\Administrator> get-sploglevel |? {$_.Area.Name -eq "Wingtip"}

Area                            Name                            TraceSev   EventSev
----                            ----                            --------   --------
Wingtip                         Product Development             Verbose    Information
Nov 2, 2011 at 9:58 PM

Good suggestion.  I modified my Wingtip.Global LoggingConfiguration feature event receiver to write to the trace log using the categories it just set up, and I see the same error (without all the sandbox outer exceptions):

Error	1	Error occurred in deployment step 'Add Solution': One or more error occurred while writing messages into the log.\r\nThe error while writing to the EventLog was:An exception has occurred.
	ExceptionType: 'LoggingException'
	ExceptionMessage: 'The Category '{0}' was not found in the diagnostic categories collections.'
	StackTrace: '   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.ParseCategoryPath(String categoryPath)
		   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.FindCategory(String categoryPath)
		   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.GetCategory(String categoryName)
		   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.LogTrace(String message, Int32 eventId, String category)
		   at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToDeveloperTrace(String message, Int32 eventId, String category)'
	Source: 'Microsoft.Practices.SharePoint.Common'
	TargetSite: 'System.String[] ParseCategoryPath(System.String)'
\r\n Orginal logged message was: An error occurred while writing tot the Trace Log, trace message:Logging areas and categories added.
		0	0	Wingtip.Global

Here's the updated event receiver:

public class LoggingConfigurationEventReceiver : SPFeatureReceiver
	{				
		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in Areas.AllAreas())
			{
				if (null == areaCollection[area.Name])
				{
					areaCollection.Add(area);
				}
				else
				{
					foreach (var category in area.DiagnosticsCategories)
					{
						var existingArea = areaCollection[area.Name];						
						if (null == existingArea.DiagnosticsCategories[category.Name])
						{
							existingArea.DiagnosticsCategories.Add(category);
						}
					}
				}
			}

			areaCollection.SaveConfiguration();

			logger.TraceToDeveloper("Logging areas and categories added.", Areas.Wingtip.Configuration);
		}

		public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in Areas.AllAreas())
			{
				DiagnosticsArea existingArea = null;
				if (null != (existingArea = areaCollection[area.Name]))
				{
					areaCollection.Remove(existingArea);
				}
			}

			areaCollection.SaveConfiguration();

			logger.TraceToDeveloper("Logging areas and categories removed.", Areas.Wingtip.Configuration);
		}
}

Nov 3, 2011 at 12:22 AM

Ok, after looking in the location of the thrown exception (<tt>ParseCategory</tt>), I realized that I wasn't formatting my category names correctly.  I'm still getting it wrong apparently, but I'm now getting past the parse method, at least:

Error	1	Error occurred in deployment step 'Add Solution': One or more error occurred while writing messages into the log.\r\nThe error while writing to the EventLog was:An exception has occurred.
	ExceptionType: 'LoggingException'
	ExceptionMessage: 'The Category 'Wingtip/Configuration' was not found in the diagnostic categories collections.'
	StackTrace: '   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.GetCategory(String categoryName)
		   at Microsoft.Practices.SharePoint.Common.Logging.DiagnosticsService.LogTrace(String message, Int32 eventId, String category)
		   at Microsoft.Practices.SharePoint.Common.Logging.SharePointLogger.WriteToDeveloperTrace(String message, Int32 eventId, String category)'
	Source: 'Microsoft.Practices.SharePoint.Common'
	TargetSite: 'Microsoft.SharePoint.Administration.SPDiagnosticsCategory GetCategory(System.String)'
\r\n Orginal logged message was: An error occurred while writing tot the Trace Log, trace message:Logging areas and categories added.
		0	0	Wingtip.Global

Looks like progress to me, but not there yet. Here is my updated <tt>Areas</tt> and feature event receiver:

 

////////////////////////////////////////////
/// Areas.cs
////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.SharePoint.Administration;

namespace Wingtip.Global.Logging
{
	public static class Areas
	{
		public static IEnumerable<DiagnosticsArea> AllAreas()
		{
			return typeof(Areas).GetNestedTypes()
				.Where(t => t.GetCustomAttributes(typeof(AreaAttribute), false).Any())
				.Select(t => BuildArea(t));
		}

		[Area(Name="Wingtip")]
		public static class Wingtip
		{
			public static string Name 
			{ 
				get 
				{
					var attr = (AreaAttribute)typeof(Wingtip).GetCustomAttributes(typeof(AreaAttribute), false).First();
					return attr.Name;
				} 
			}

			[Category(DefaultEventSeverity = EventSeverity.Information, DefaultTraceSeverity = TraceSeverity.Verbose)]
			public static string ProductDevelopment
			{
				get { return Name + @"/Product Development"; }
			}

			[Category(DefaultTraceSeverity=TraceSeverity.Verbose, DefaultEventSeverity=EventSeverity.Information)]
			public static string Configuration
			{
				get { return Name  + @"/Configuration"; }
			}
		}

		private static DiagnosticsArea BuildArea(Type areaType)
		{
			var cats = new DiagnosticsCategoryCollection();
			foreach (var category in areaType.GetProperties().Where(p => p.GetCustomAttributes(typeof(CategoryAttribute), false).Any()))
			{
				var catAttr = category.GetCustomAttributes(typeof(CategoryAttribute), false).First() as CategoryAttribute;
				cats.Add(new DiagnosticsCategory(category.GetValue(null, null) as string,
					catAttr.DefaultEventSeverity, catAttr.DefaultTraceSeverity));
			}

			AreaAttribute areaAttr = areaType.GetCustomAttributes(typeof(AreaAttribute), false).First() as AreaAttribute;
			return new DiagnosticsArea(areaAttr.Name, cats);
		}
	}
}


////////////////////////////////////////
/// LoggingConfigurationEventReceiver.cs
////////////////////////////////////////

using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;

using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Wingtip.Global.Logging;

namespace Wingtip.Global.Features.LoggingConfiguration
{
	[Guid("5f49c1dd-9204-4c55-9802-ccdc9061f42b")]
	public class LoggingConfigurationEventReceiver : SPFeatureReceiver
	{				
		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in Areas.AllAreas())
			{
				if (null == areaCollection[area.Name])
				{
					areaCollection.Add(area);
				}
				else
				{
					foreach (var category in area.DiagnosticsCategories)
					{
						var existingArea = areaCollection[area.Name];						
						if (null == existingArea.DiagnosticsCategories[category.Name])
						{
							existingArea.DiagnosticsCategories.Add(category);
						}
					}
				}
			}

			areaCollection.SaveConfiguration();

			logger.TraceToDeveloper("Logging areas and categories added.", Areas.Wingtip.Configuration);
		}

		public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in Areas.AllAreas())
			{
				DiagnosticsArea existingArea = null;
				if (null != (existingArea = areaCollection[area.Name]))
				{
					areaCollection.Remove(existingArea);
				}
			}

			areaCollection.SaveConfiguration();

			logger.TraceToDeveloper("Logging areas and categories removed.", Areas.Wingtip.Configuration);
		}
	}
}

Because I haven't yet figured out how to avoid the issue of local variables and references being optimized away, I haven't yet pinpointed the issue, but I'm fairly sure it's in the expression beginning with <tt>SPDiagnosticsCategory foundCategory</tt>:

        /// <summary>
        /// Finds the desired SharePoint diagnostic category based on the name passed in.
        /// </summary>
        /// <param name="categoryPath">>The name of the diagnostic category.</param>
        /// <returns>The sharepoint diagnostics category found.</returns>
        [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public virtual SPDiagnosticsCategory FindCategory(string categoryPath)
        {
            Validation.ArgumentNotNullOrEmpty(categoryPath, "categoryPath");

            string[] categoryPathElements = ParseCategoryPath(categoryPath);
            string areaName = categoryPathElements[0];
            string categoryName = categoryPathElements[1];

            SPDiagnosticsCollection<SPDiagnosticsArea> areas = this.Areas;

            if (areas == null)
                return DefaultCategory;

            foreach (SPDiagnosticsArea area in areas)
            {
                if (areaName.Trim().Equals(area.Name.Trim(), StringComparison.OrdinalIgnoreCase))
                {
                    SPDiagnosticsCategory foundCategory = area.Categories.FirstOrDefault<SPDiagnosticsCategory>(delegate(SPDiagnosticsCategory category)
                    {
                        return categoryName.Equals(category.Name, StringComparison.OrdinalIgnoreCase);
                    });

                    return foundCategory;
                }
            }

            return null;
        }

Nov 3, 2011 at 1:25 AM

*sigh*.  After all that, I figured out that I was just never in sync with how I was adding categories and then how I was referencing them later.  Here's the updated code for any poor soul who may try this same approach:

/// LoggingConfigurationEventReceiver.cs
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;

using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Wingtip.Global.Logging;

namespace Wingtip.Global.Features.LoggingConfiguration
{
	/// <summary>
	/// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
	/// </summary>
	/// <remarks>
	/// The GUID attached to this class may be used during packaging and should not be modified.
	/// </remarks>

	[Guid("5f49c1dd-9204-4c55-9802-ccdc9061f42b")]
	public class LoggingConfigurationEventReceiver : SPFeatureReceiver
	{				
		public override void FeatureActivated(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			foreach (var area in Areas.AllAreas())
			{
				if (null == areaCollection[area.Name])
				{
					areaCollection.Add(area);
				}
				else
				{
					foreach (var category in area.DiagnosticsCategories)
					{
						var existingArea = areaCollection[area.Name];						
						if (null == existingArea.DiagnosticsCategories[category.Name])
						{
							existingArea.DiagnosticsCategories.Add(category);
						}
					}
				}
			}

			areaCollection.SaveConfiguration();

			logger.TraceToDeveloper("Logging areas and categories added.", 
				String.Format("{0}/{1}", Areas.Wingtip.Name, Areas.Wingtip.Configuration));
		}

		public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
		{
			IConfigManager configMgr = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
			ILogger logger = SharePointServiceLocator.GetCurrent().GetInstance<ILogger>();
			var areaCollection = new DiagnosticsAreaCollection(configMgr);

			logger.TraceToDeveloper("Removing areas and categories.",
				String.Format("{0}/{1}", Areas.Wingtip.Name, Areas.Wingtip.Configuration));

			foreach (var area in Areas.AllAreas())
			{
				DiagnosticsArea existingArea = null;
				if (null != (existingArea = areaCollection[area.Name]))
				{
					areaCollection.Remove(existingArea);
				}
			}

			areaCollection.SaveConfiguration();
		}

	}
}

/// Areas.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.SharePoint.Administration;

namespace Wingtip.Global.Logging
{
	public static class Areas
	{
		public static IEnumerable<DiagnosticsArea> AllAreas()
		{
			return typeof(Areas).GetNestedTypes()
				.Where(t => t.GetCustomAttributes(typeof(AreaAttribute), false).Any())
				.Select(t => BuildArea(t));
		}

		[Area(Name="Wingtip")]
		public static class Wingtip
		{
			public static string Name 
			{ 
				get 
				{
					var attr = (AreaAttribute)typeof(Wingtip).GetCustomAttributes(typeof(AreaAttribute), false).First();
					return attr.Name;
				} 
			}

			[Category(DefaultEventSeverity = EventSeverity.Information, DefaultTraceSeverity = TraceSeverity.Verbose)]
			public static string ProductDevelopment
			{
				get { return "Product Development"; }
			}

			[Category(DefaultTraceSeverity=TraceSeverity.Verbose, DefaultEventSeverity=EventSeverity.Information)]
			public static string Configuration
			{
				get { return "Configuration"; }
			}			
		}

		private static DiagnosticsArea BuildArea(Type areaType)
		{
			var cats = new DiagnosticsCategoryCollection();
			foreach (var category in areaType.GetProperties().Where(p => p.GetCustomAttributes(typeof(CategoryAttribute), false).Any()))
			{
				var catAttr = category.GetCustomAttributes(typeof(CategoryAttribute), false).First() as CategoryAttribute;
				cats.Add(new DiagnosticsCategory(category.GetValue(null, null) as string,
					catAttr.DefaultEventSeverity, catAttr.DefaultTraceSeverity));
			}

			AreaAttribute areaAttr = areaType.GetCustomAttributes(typeof(AreaAttribute), false).First() as AreaAttribute;
			return new DiagnosticsArea(areaAttr.Name, cats);
		}
	}
}

As for me, my next stop is to see about getting satellite resource assemblies going instead of this craptacular static class arrangement.

Coordinator
Nov 3, 2011 at 11:33 AM

I'm glad you found it.  This is an area of the API that is unfortunately not that intuitive, and doesn't use compile time checking so it can be hard more challenging to isolate.  In hindsight I would have designed it differently.