Unity, Part 11: Integrating With Azure Application Insights

Another one for the Unity series.

Lately I’ve been playing with Azure Application Insights. Nice thing, even if – at least, for the moment – not as powerful as some of its contesters. A thing that came to my mind almost immediately was how to integrate it with IoC containers like Unity.

I already talked about how to use AOP techniques in Unity. This time I will leverage on that and explain how we can use this knowledge to add insights into our application transparently.

We need the Application Insights SDK, which is available at GitHub in source code and conveniently as a NuGet package (all you need is Microsoft.ApplicationInsights):

image

I implemented an HandlerAttribute that is also an implementation of ICallHandler. Inside of it, I call the intercepted method and then log it to Application Insights through the TelemetryClient, a part of the Application Insights APIs. I added an option to set the instrumentation key, which uniquely identifies our Application Insights account and shouldn’t be shared. If not supplied, it will default to whatever is in

TelemetryConfiguration.Active.InstrumentationKey. Finally, we can decide to have the call asynchronous (so as to not cause delays to our application) or synchronous.

Here is the code for the interception attribute:

[Serializable]

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

public sealed class TelemetryCallHandlerAttribute : HandlerAttribute, ICallHandler

{

    #region Public constructors

    public TelemetryCallHandlerAttribute()

    {

    }

 

    public TelemetryCallHandlerAttribute(string instrumentationKey)

    {

        this.InstrumentationKey = instrumentationKey;

    }

 

    public string InstrumentationKey { get; set; }

 

    public bool Async { get; set; }

 

    #endregion

 

    #region Public override methods

    public override ICallHandler CreateHandler(IUnityContainer ignored)

    {

        return (this);

    }

    #endregion

 

    #region ICallHandler Members

 

    IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

    {

        TelemetryConfiguration config = null;

 

        if (string.IsNullOrWhiteSpace(this.InstrumentationKey) == true)

        {

            config = TelemetryConfiguration.Active;

        }

        else

        {

            config = TelemetryConfiguration.CreateDefault();

            config.InstrumentationKey = this.InstrumentationKey;

        }

 

        var telemetryClient = new TelemetryClient(config);

        var watch = Stopwatch.StartNew();

        var result = getNext()(input, getNext);

 

        var elapsedMilliseconds = watch.ElapsedMilliseconds;

        var exception = result.Exception;

        var returnValue = result.ReturnValue;

 

        var properties = new Dictionary<string, string>();

 

        for (var i = 0; i < input.Arguments.Count; ++i)

        {

            var key = input.Arguments.ParameterName(i);

            properties[key] = (input.Arguments[i] ?? string.Empty).ToString();

        }

 

        if (exception != null)

        {

            properties["$Exception"] = exception.Message;

        }

 

        if (returnValue != null)

        {

            properties["$ReturnValue"] = returnValue.ToString();

        }

 

        var metrics = new Dictionary<string, double>();

        metrics["ElapsedMilliseconds"] = elapsedMilliseconds;

 

        if (this.Async == false)

        {

            this.TrackEvent(telemetryClient, input.MethodBase.Name, properties, metrics);

        }

        else

        {

            this.TrackEventAsync(telemetryClient, input.MethodBase.Name, properties, metrics);

        }

 

        return (result);

    }

 

    private void TrackEvent(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

    {

        telemetryClient.TrackEvent(name, properties, metrics);

    }

 

    private async void TrackEventAsync(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

    {[Serializable]

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

    public sealed class TelemetryCallHandlerAttribute : HandlerAttribute, ICallHandler

    {

        #region Public constructors

        public TelemetryCallHandlerAttribute()

        {

        }

 

        public TelemetryCallHandlerAttribute(string instrumentationKey)

        {

            this.InstrumentationKey = instrumentationKey;

        }

 

        public string InstrumentationKey { get; set; }

 

        public bool Async { get; set; }

 

        #endregion

 

        #region Public override methods

        public override ICallHandler CreateHandler(IUnityContainer ignored)

        {

            return (this);

        }

        #endregion

 

        #region ICallHandler Members

 

        IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)

        {

            TelemetryConfiguration config = null;

 

            if (string.IsNullOrWhiteSpace(this.InstrumentationKey) == true)

            {

                config = TelemetryConfiguration.Active;

            }

            else

            {

                config = TelemetryConfiguration.CreateDefault();

                config.InstrumentationKey = this.InstrumentationKey;

            }

 

            var telemetryClient = new TelemetryClient(config);

            var watch = Stopwatch.StartNew();

            var result = getNext()(input, getNext);

 

            var elapsedMilliseconds = watch.ElapsedMilliseconds;

            var exception = result.Exception;

            var returnValue = result.ReturnValue;

 

            var properties = new Dictionary<string, string>();

 

            for (var i = 0; i < input.Arguments.Count; ++i)

            {

                var key = input.Arguments.ParameterName(i);

                properties[key] = (input.Arguments[i] ?? string.Empty).ToString();

            }

 

            if (returnValue != null)

            {

                properties["$ReturnValue"] = returnValue.ToString();

            }

 

            var metrics = new Dictionary<string, double>();

            metrics["ElapsedMilliseconds"] = elapsedMilliseconds;

 

            if (this.Async == false)

            {

                if (exception != null)

                {

                    properties["Name"] = input.MethodBase.Name;

                    this.TrackException(telemetryClient, exception, properties, metrics);

                }

                else

                {

                    this.TrackEvent(telemetryClient, input.MethodBase.Name, properties, metrics);

                }

            }

            else

            {

                if (exception != null)

                {

                    properties["Name"] = input.MethodBase.Name;

                    this.TrackExceptionAsync(telemetryClient, exception, properties, metrics);

                }

                else

                {

                    this.TrackEventAsync(telemetryClient, input.MethodBase.Name, properties, metrics);

                }

            }

 

            return (result);

        }

 

        private void TrackException(TelemetryClient telemetryClient, Exception ex, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            telemetryClient.TrackException(ex, properties, metrics);

        }

 

        private async void TrackExceptionAsync(TelemetryClient telemetryClient, Exception ex, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            await Task.Run(() => this.TrackException(telemetryClient, ex, properties, metrics));

        }

 

        private void TrackEvent(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            telemetryClient.TrackEvent(name, properties, metrics);

        }

 

        private async void TrackEventAsync(TelemetryClient telemetryClient, string name, IDictionary<string, string> properties, IDictionary<string, double> metrics)

        {

            await Task.Run(() => this.TrackEvent(telemetryClient, name, properties, metrics));

        }

 

        #endregion

    }        await Task.Run(() => this.TrackEvent(telemetryClient, name, properties, metrics));

    }

 

    #endregion

}

It will track the event under the called method name, and will send along a string representation of all its arguments, result value, exception thrown (if any) and elapsed time (TelemetryClient.TrackEvent or TelemetryClient.TrackException).

A simple usage, without providing the instrumentation key, would be:

[TelemetryCallHandler]

public virtual BusinessResponse PerformBusinessOperation(int businessId, string arg)

{

    //...

}

If the InstrumentationKey property is not supplied, it must be set through TelemetryConfiguration.Active.InstrumentationKey:

TelemetryConfiguration.Active.InstrumentationKey = "my key";

Having it as an IInterceptionBehavior should be straightforward. Feel free to modify it to your liking!

Unity, Part 10: Custom Build Strategies

Introduction

We’re getting there! This time, custom build strategies, or how you can tell Unity to build/act upon built objects in different ways.

The latest post in the series was on integration with MEF, the previous on web, before that on interfaces, even before I talked about conventions, values, extensions, Aspect-Oriented Programming, dependency injection and the first was an introduction.

Unity allows specifying custom build strategies. Depending on the stage to which they are applied, they can either build objects for us or do something upon newly built ones.

Performing Operations On Newly Built Objects

One example of the latter: suppose we wanted to implement support for ISupportInitialize (I repeat myself, I know). This is a marker interface that features two methods: BeginInit for signaling the start of the initialization process and EndInit for its end. We need a custom build strategy that knows when to call each of these methods. We add build strategies through extensions:

   1: public sealed class SupportInitializeContainerExtension : UnityContainerExtension

   2: {

   3:     protected override void Initialize()

   4:     {

   5:         var strategy = new SupportInitializeBuilderStrategy();

   6:         this.Context.Strategies.Add(strategy, UnityBuildStage.Creation);

   7:     }

   8: }

Notice how we add our build strategy to the Creation build stage. This tells Unity that the strategy’s lifecycle methods should be called before and after the object is created. It’s important to remember that only components registered with RegisterType will be intercepted, because those registered with RegisterInstance are, of course, already built.

Extensions are added to the Unity instance:

   1: unity.AddNewExtension<SupportInitializeContainerExtension>();

The build strategy itself inherits from BuilderStrategy, which in turn implements IBuilderStrategy; it’s implementation is straightforward:

   1: public sealed class SupportInitializeBuilderStrategy : BuilderStrategy

   2: {

   3:     public override void PreBuildUp(IBuilderContext context)

   4:     {

   5:         var init = context.Existing as ISupportInitialize;

   6:  

   7:         if (init != null)

   8:         {

   9:             init.BeginInit();

  10:         }

  11:  

  12:         base.PreBuildUp(context);

  13:     }

  14:  

  15:     public override void PostBuildUp(IBuilderContext context)

  16:     {

  17:         var init = context.Existing as ISupportInitialize;

  18:  

  19:         if (init != null)

  20:         {

  21:             init.EndInit();

  22:         }

  23:  

  24:         base.PostBuildUp(context);

  25:     }

  26: }

PreBuildUp and PostBuildUp are called in sequence just after the object is built (the Existing property). Other lifetime methods exist, which are called at different times, depending on which stage the builder was added to.

Overriding Object Creation

Another example would be intercepting object creation. For that we need another extension:

   1: public sealed class CustomBuildExtension : UnityContainerExtension

   2: {

   3:     public Func<Type, Type, String, MethodBase, IUnityContainer, Object> Constructor { get; set; }

   4:  

   5:     protected override void Initialize()

   6:     {

   7:         var strategy = new CustomBuilderStrategy(this);

   8:         this.Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);

   9:     }

  10: }

The Constructor delegate property takes a number of parameters and returns an object instance. These parameters are:

  • First Type: the registered type, such as ILogger;
  • Second Type: the concrete type mapped, an ILogger implementation such as ConsoleLogger;
  • String: the registered name, such as “console”, or null;
  • MethodBase: the method (or property getter/setter) where the component resolution was requested, for context;
  • IUnityContainer: the current Unity instance;

We would register it as:

   1: var extension = new CustomBuildExtension();

   2: extension.Constructor = (from, to, name, method, u) =>

   3: {

   4:     //return something

   5: };

   6:  

   7: unity.AddExtension(extension);

The strategy in this case would be like this:

   1: public sealed class CustomBuilderStrategy : BuilderStrategy

   2: {

   3:     private readonly CustomBuildExtension extension;

   4:     

   5:     public CustomBuilderStrategy(CustomBuildExtension extension)

   6:     {

   7:         this.extension = extension;

   8:     }

   9:     

  10:     private IUnityContainer GetUnityFromBuildContext(IBuilderContext context)

  11:     {

  12:         var lifetime = context.Policies.Get<ILifetimePolicy>(NamedTypeBuildKey.Make<IUnityContainer>());

  13:         return lifetime.GetValue() as IUnityContainer;

  14:     }

  15:     

  16:     public override void PreBuildUp(IBuilderContext context)

  17:     {

  18:         var stackTrace = new StackTrace();

  19:         var frame = stackTrace.GetFrame(6);

  20:         var method = frame.GetMethod();

  21:         var fromType = context.OriginalBuildKey.Type;

  22:         var name = context.OriginalBuildKey.Name;

  23:         var toType = context.BuildKey.Type;

  24:         var unity = this.GetUnityFromBuildContext(context);

  25:     

  26:         context.Existing = this.extension.Constructor(fromType, toType, name, method, unity);

  27:         context.BuildComplete = true;

  28:     

  29:         var lifetimeManager = new ContainerControlledLifetimeManager();

  30:         lifetimeManager.SetValue(context.Existing);

  31:     

  32:         context.Lifetime.Add(lifetimeManager);

  33:     

  34:         base.PreBuildUp(context);

  35:     }

  36: }

Worth mentioning:

  • A StackTrace instance is used to walk back the stack until our custom method was called;
  • The from type, name and to type are obtained from the OriginalBuildKey and BuildKey;
  • The Unity instance is a bit more tricky to get, it comes from the current Policies;
  • Existing and BuildComplete are set so that the build process is terminated with this builder;
  • The Constructor delegate is invoked and should return a proper object inheriting (or implementing) from from type;
  • A ContainerControlledLifetimeManager (aka, singleton) is used to track the new object lifetime and added to Unity’s Lifetime collection, so that when Unity is disposed of, the lifetime manager also gets disposed.

And that’s it. This way, you can decide how your object is going to be built and even on which method is it being requested. Hope you find this useful! Winking smile

Unity, Part 9: Integration With Managed Extensibility Framework

This time, I will be talking about integrating Unity with Managed Extensibility Framework (MEF). You can find the other posts in the series here (how to use Unity in a web application), here (adding Interfaces), here (registration by convention), here (injecting values), here (extensions), here (aspect-oriented programming), here (dependency injection) and the first one here (introduction).

The Managed Extensibility Framework (MEF) has been around since the release of .NET 4.0, and even before as a beta, stand-alone package. Basically, it provides an extensible mechanism for detecting and loading plugins. It’s easier to use than the similarly-named Managed Add-In Framework (MAF), and even if it’s not so feature-rich (it doesn’t support sandboxing, for once), unlike MAF, it is well alive!

So, what does MEF offer that can be of use to Unity? Well, MEF knows how to locate exports/plugins from a number of locations, like assemblies and file system directories. It’s just a matter of finding the exports we’re interested in and registering them with Unity.

An export in MEF is some class that is decorated with an ExportAttribute (technically speaking, this is just when using the Attributed Programming Model, since .NET 4.5 there is also the Convention-Based Programming Model). This attribute allows specifying the type to export (ContractType) and also the contract name (ContractName). This matches closely the Unity/IoC concept of contract type and name.

We could find all exports under a given path using MEF using an AssemblyCatalog, a particular implementation of a ComposablePartCatalog:

   1: var catalog = new AssemblyCatalog("some path");

A couple of helper functions for picking up the export’s contract type and name, by leveraging the ReflectionModelServices class:

   1: public static IDictionary<String, Type> GetExportedTypes<T>(this ComposablePartCatalog catalog)

   2: {

   3:     return (GetExportedTypes(catalog, typeof(T)));

   4: }

   5:  

   6: public static IDictionary<String, Type> GetExportedTypes(this ComposablePartCatalog catalog, Type type)

   7: {

   8:     return (catalog.Parts.Where(part => IsComposablePart(part, type) == true).ToDictionary(part => part.ExportDefinitions.First().ContractName, part => ReflectionModelServices.GetPartType(part).Value));

   9: }

  10:  

  11:  

  12: private static Boolean IsComposablePart(ComposablePartDefinition part, Type type)

  13: {

  14:     return (part.ExportDefinitions.Any(def => (def.Metadata.ContainsKey("ExportTypeIdentity") == true) && (def.Metadata["ExportTypeIdentity"].Equals(type.FullName) == true)));

  15: }

This will return a collection of key-value pairs, where the key is the contract name and the value the contract type; this is so there can be multiple contract names for a given contract type. After we have this, it’s just a matter of iterating the results and registering each occurrence:

   1: var type = typeof(ISomeType);

   2: var exports = catalog.GetExportedTypes(type);

   3:  

   4: foreach (var entry in exports)

   5: {

   6:     unity.RegisterType(type, entry.Value, entry.Key);

   7: }

So, given the following contract and implementations:

   1: public interface ISomeType

   2: {

   3:     void SomeMethod();

   4: }

   5:  

   6: [Export("Some", typeof(ISomeType))]

   7: public class SomeImplementation : ISomeType

   8: {

   9:     public void SomeMethod() { }

  10: }

  11:  

  12: [Export("Another", typeof(ISomeType))]

  13: public class AnotherImplementation : ISomeType

  14: {

  15:     public void SomeMethod() { }

  16: }

We can obtain a specific contract type implementation given it’s name:

   1: var myImplementation = unity.Resolve<ISomeType>("MyName");

And also all implementations of the contract that were found:

   1: var all = unity.ResolveAll<ISomeType>();

This can be enhanced in a couple of ways:

  • Use a Unity extension to automatically find and register exports at runtime;
  • Make use of MEF metadata to tell Unity which lifetime managers to use, and other useful properties, such as the default implementation for the contract type.

As usual, I’m looking forward for your comments!