Configuring PostSharp Diagnostics Toolkits

by Igal Tabachnik on 03 Apr 2012

One of the ideas behind the PostSharp Toolkits was a zero code change requirement, that would allow you to simply install the relevant toolkit from NuGet, rebuild your project and that’s it. To achieve that, we have revived the PostSharp XML configuration. The XML configuration is the unification of the Plug-in and Project models in the project loader. Let’s have a look on how PostSharp Diagnostics Toolkits use this XML configuration.

PSPROJ XML Configuration

After installing the PostSharp Diagnostics Toolkits via NuGet, a file with the .psproj extension will be created, named after the current project. The .psproj files are an XML representation of the PostSharp Project structure – containing the configuration of the application, with resolved properties and references.

Let’s take a look at the default configuration that is produced by the PostSharp Diagnostic Toolkit package:

<?xml version="1.0" encoding="utf-8" ?>
<!-- Default project used when PostSharp is detected according to project references. -->
<Project xmlns="https://schemas.postsharp.org/1.0/configuration" 
ReferenceDirectory="{$ReferenceDirectory}">  
 
<Property Name="LoggingBackend" Value="trace" />
 
<Using File="default"/>
<Using File="..\..\Build\bin\{$Configuration}\PostSharp.Toolkit.Diagnostics.Weaver.dll"/>
<Tasks>
   <XmlMulticast />
</Tasks>

<Data Name="XmlMulticast">
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics" />
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
AttributeExclude="true"
AttributeTargetMembers="regex:get_.*|set_.*" /> 
</Data>
</Project>

Let’s look at the properties more closely:

Project – the XML root node – defines the PostSharp Project configuration.

The property LoggingBackend specifies the logger that should be used by the Diagnostics Toolkit. This value is set during the installation of the toolkit via NuGet. The supported values in the PostSharp Diagnostics Toolkit package are trace (default) and console. Additional packages, PostSharp Diagnostics Toolkit for Log4Net and NLog add log4net and nlog as the supported backends.

The Using directives are a part of the plug-in model, allowing PostSharp to use external services. In this case, there are two entries (additional entries are added by other toolkits), the default, which is a required entry, and a (relative) path to the actual weaver implementation of our toolkit. At build time, PostSharp looks for project configurations in the referenced DLLs.

The Tasks section specifies a key feature of the XML configuration – the XML Multicasting. Like regular aspect multicasting, that is, the ability to apply a single aspect to multiple elements, XML Multicasting allows you to define aspect multicasting declaratively via XML. The XmlMulticast task will look for the data island with the name XmlMulticast, instantiate and apply the aspects that are specified within.

Which brings us to the actual LogAttribute aspect, that is shipped with the toolkits. This is a custom MethodLevelAspect, that is defined in the assembly PostSharp.Toolkit.Diagnostics.dll. The two entries in the XML file above define that the aspect will be applied by default to a) all methods in all types of the current assembly and b) will ignore property getters and setters. These lines are equivalent to applying the aspect on assembly level in code:

[assembly: Log] 
    
[assembly: Log(AttributeExclude="true" AttributeTargetMembers="regex:get_.*|set_.*")]

You can limit the multicasting by using the regular PostSharp filters, such as AttributeTargetTypes.

Logging Options

To control the logging level, severity and granularity of the logging, the Diagnostics toolkits include several options for fine-grained control over the logging output:

OnEntryLevel/OnSuccessLevel/OnExceptionLevel – specifies the logging level (severity) of the Entry/Exit/Exception message (e.g. “Entering: MyType.MyMethod()/Leaving: MyType.MyMethod()”)

Possible values:

  • None – the message will not be logged
  • Debug – the message will be logged at Debug/Trace level (when applicable)
  • Info – the message will be logged at Info level
  • Warning – the message will be logged at Warn level
  • Error – the message will be logged at Error level
  • Fatal – the message will be logged at Fatal level

OnEntryOptions/OnSuccessOptions – sets options for logging parameters and return values.

The options include:

  • None – no parameter information will be included
  • IncludeParameterType – includes the type name of the parameter
  • IncludeParameterName – includes the name of the parameter
  • IncludeParameterValue - Includes parameter value, by calling ToString on the object instance
  • IncludeReturnValue – includes the return value (applicable on OnSuccessOptions only)
  • IncludeThisArgument – includes the value of this argument in an instance method

The default values for the different options are:

Option Name Default Value
OnEntryLevel Debug
OnSuccessLevel Debug
OnExceptionLevel Warning
OnEntryOptions IncludeParameterType, IncludeParameterName, IncludeParameterValue
OnSuccessOptions IncludeParameterType, IncludeReturnValue
OnExceptionOptions None (exception is printed using the OnExceptionLevel severity)

Examples:

Suppose we have a method Reverse in class StringUtils, that takes in a string and returns a reversed string. With the default settings, using NLog as the backend, our call to this method with the word “orange” will be logged like this:

 

...

TRACE Entering: MyApplication.StringUtils.Reverse(System.String input = "orange")
TRACE Leaving: MyApplication.StringUtils.Reverse(System.String) : "egnaro"
...

 

In the Entering line, the method signature contains the type name (System.String), the parameter name (“input”) and its value. The Leaving line contains only the parameter type and the return value.

Let’s look at another example. Suppose you have a class Customer, implementing an Active Record pattern, located in the namespace MyApplication.Data. Suppose we want to log all calls made to methods in this namespace with the Info level, and having the value of the instance (this argument) printed in the log output together with the value of the parameters. Simply add the following line to the .psproj file:

 

<LogAttribute
    xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
    AttributeTargetTypes="MyApplication.Data.*"
    OnEntryLevel="Info"
    OnSuccessLevel="Info"
    OnEntryOptions="IncludeThisArgument | IncludeParameterValue" />

Additional notes

In the toolkits we’re done away with manually specifying the ordering using AttributePriority – the value is now generating automatically during compilation, so be aware that ordering of the XML elements matters.

We hope that you’ll enjoy using the PostSharp Toolkits as much as we enjoy building them!

Happy PostSharping!

-Igal