Getting Started With Profiler

This page provides information on installing and configuring the profiler.

Web.config entries

The profiler requires an HTTP handler and an HTTP module to run. The handler is responsible for processing requests to the admin interface, and the module is responsible for determining whether to profile the current request. The code below shows the two entries you need to add to your web.config file.

HTTP Handler

<add verb="*" path="profiler" type="ProductionProfiler.Core.Handlers.ProfilerAdministrationHandler, ProductionProfiler.Core"/>

HTTP Module

<add name="profiler" type="ProductionProfiler.Core.Modules.RequestProfilingModule, ProductionProfiler.Core"/>

Configuration

Configuration of the profiler is configured via a fluent interface based upon the Configure class. The following code snippet shows an example configuration of the profiler.

Configure.With(container)
    .HandleExceptionsVia(e => System.Diagnostics.Trace.Write(e.Format()))
    .Logger(new Log4NetLogger())
    .DataProvider(new SqlPersistenceProvider(new SqlConfiguration("profiler", "profiler", "dbo")))
    .HttpRequestDataCollector<BasicHttpRequestDataCollector>()
    .HttpResponseDataCollector<BasicHttpResponseDataCollector>()
    .TypesToIntercept(new[] { typeof(IWorkflow) })
    .TypesToIgnore(new[] { typeof(IController) })
    .CollectMethodDataForTypes(new[] { typeof(IWorkflow) })
    .AddMethodInvocationDataCollector<WorkflowMethodInvocationDataCollector>()
        .ForTypesAssignableFrom(new []{typeof(IWorkflow)})
    .CacheEngine<HttpRuntimeCacheEngine>()
    .RequestFilter(req => Path.GetExtension(req.Url.AbsolutePath) == string.Empty)
    .Trigger
        .BySession()
    .Trigger
        .ByUrl()
    .Trigger
        .BySampling()
            .Every(new TimeSpan(0, 0, 30))
            .For(new TimeSpan(0, 0, 30))
            .Enable()
    .Serializer<JsonSerializer>()
    .AuthoriseManagement(context => Context.User.IsInRole("Administrator"))
    .AuthoriseSession(sessionId => sessionId == "checksessionidisvalid")
    .Initialise();

 

There is a fair bit going on here, so here's a step by step guide to all the options we are setting up, starting from the top.

Configure.With(IContainer container)

The profiler works by intercepting components resolved by the IoC container running your application. The profiler currently supports Castle Windsor and StructureMap. The IContainer interface abstracts your container of choice from the profiler, an instance of IContainer must be passed to the Configure.With method. This should be an instance of ProductionProfiler.IoC.Windsor.WindsorProfilerContainer for Castle or ProductionProfiler.IoC.StructureMap.StructureMapProfilerContainer if you are using StructureMap. Of course you can provide an implementation of IContainer if you are using another IoC provider.

.HandleExceptionsVia(Action<Exception> exceptionHandler)

HandleExceptionsVia method allows you to respond to any exceptions originating within the profiler codebase. The profiler has been designed so no exceptions will be thrown by the profiler. This is to prevent it from interfering with your application. Therefore any exceptions will be reported back to your application via the Action passed to this method.

.Logger(new Log4NetLogger())

The Logger method takes a component implementing ILogger. Currently the profiler only support Log4net. The purpose of this interface is to capture any logging information your application outputs, be it diagnostics trace information, exceptions, warnings etc. With Log4net this works by inspecting your Log4net configuration and adding a custom IAppender (ProductionProfiler.Core.Logging.Log4NetProfilingAppender) to each configured logger.

For this to work correctly, you should set up your Log4net configuration so your threshold levels are set at the appender level rather than on the loggers themselves. For example, for any logging information you want the profiler to capture, the logger itself should have its priority value set to VERBOSE, the appenders configured to the logger can then specify their threshold levels to the required value. An instance of Log4NetProfilingAppender is added to each logger with a Log Level of VERBOSE, in order to capture all information written to the logger. See below for an example Log4net configuration.

<log4net debug="false">
  <appender name="TraceAppender" type="log4net.Appender.TraceAppender">
    <threshold value="ERROR" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%d - %m%n" />
    </layout>
  </appender>

  <logger name="MyLogger">
    <appender-ref ref="TraceAppender"/>
    <priority value="VERBOSE"/>
  </logger>
</log4net>

In this example only errors will be output to the TraceAppender, however once the profiler has initialized, an additional appender will have been added to the MyLogger which will have its threshold value set to VERBOSE, this is the appender that the profiler uses to capture all your logging information.  

.DataProvider(IPersistenceProvider provider)

The IPersistenceProvider interface is used to tell the profiler which data store to use for persisting data required by the profiler. Currently the profiler supports SQL Server, SQLite, MongoDB and RavenDB.

New persistence providers can be created by implementing the following interfaces.

  • IPersistenceProvider
  • IProfilerRepository

.HttpRequestDataCollector<T>()

The HttpRequestDataCollector method allows you to give the profiler a type to use to collect information about the HTTP request which triggers the profile. There is an out of the box collector (ProductionProfiler.Core.Collectors.BasicHttpRequestDataCollector) which adds all form parameters, headers and cookies to the profiled data. You can provide your own implementation of IHttpRequestDataCollector if you want to change the behaviour and or data collected for the request.

.HttpResponseDataCollector<T>()

Works the same as above, the default implementation of IHttpResponseDataCollector (ProductionProfiler.Core.Collectors.BasicHttpResponseDataCollector) collects response headers and cookies, a custom implementation of  IHttpResponseDataCollector can be configured using this method.

.TypesToIntercept(IEnumerable<Type> types)

Use this method to tell the profiler which types we should intercept and profile. If no types are specified (i.e. you don't invoke the method as part of the configuration) all components resolved from the container will be intercepted and profiled. The profile will intercept all method calls on the supplied types. You can use base interfaces as the types to intercept and the profiler will determine whether the component resolved from the container should be profiled by checking to see whether the type is assignable from the type provided to this method (i.e.  _typesToIntercept.Any(t => t.IsAssignableFrom(resolvedType)).

To simplify the usage of the profiler I have on existing projects defined a marker interface (IWantToBeProfiled for example) then added this interface to any types I want to profile.

.TypesToIgnore(IEnumerable<Type> types)

If you do not specify any types to intercept, the profiler will intercept all components resolved via your container. If this is how you have configured the profiler you may want to exclude particular interfaces / types, you can use this method to tell the profiler which types to ignore.

.CollectMethodDataForTypes(IEnumerable<Type> types)

This method allows you to tell the profiler which types it should collect method data for. Method data includes any arguments supplied to the method and any values returned by the method. This works by serializing the arguments and returned values and storing them as DataCollectionItems against the method invocation data collected by the profiler. The serializer used can be configured (see .Serializer<T>() method below), support for XML and JSON serialization is supported.

.AddMethodInvocationDataCollector<T>()

This method allows you to add custom data collectors for particular types. If for example you have a component which you want to collect custom data for you can implement the IMethodInvocationDataCollector interface for that type and use this method to tell the profiler which types to use that IMethodInvocationDataCollector for.

For any given IMethodInvocationDataCollector implementation you can use one of three configuration methods to tell the profiler which types this data collector should be used for.

  • .ForAnyType() - the profiler will use the specicfed IMethodInvocationDataCollector for all types it profiles
  • .ForTypesAssignableFrom(IEnumerable<Type> types) - the profiler will use the specified IMethodInvocationDataCollector for any types assignable from the list of types supplied
  • ForAnyUnmappedType() - the profiler will use the specicfed IMethodInvocationDataCollector for all types you have not already mapped by previous invocations of the AddMethodInvocationDataCollector.

.CacheEngine<T>() where T : IProfilerCacheEngine

The profiler uses caching for its admin interface and for maintaining a set of URL to profile to prevent database look ups on each request. The default implementation of IProfilerCacheEngine uses the HTTP runtime cache, you can provide your own implementation of the IProfilerCacheEngine and tell the profiler to use it via this configuration method.

.RequestFilter(Finc<HttpRequest, bool> filter)

This method allows you to provide a filter over which requests the profiler will run, this is not to determine whether we should profile a particular request, but to prevent the profiler from running at all for requests for images or other resources. The profilers HTTP Module will return immediately if the current request does match this filter.

 

Last edited Sep 29, 2011 at 8:06 AM by nchampion, version 8

Comments

No comments yet.