Logging everything that is going on when an assembly loads using CThru
Whist trying to work out if there is any way to get around the problem I am suffering with Sharepoint workflows idling inside a Typemock Isolator test harness I have been having a good look at CThru; a set if libraries for Typemock that, and I quote Codeplex here, ‘… allows creating interception and isolation frameworks for logging, testing and many other things very simply’. This is the framework is used to create the Silverlight mocking frame work on the same Codeplex site.
To aid my analysis I wrote a basic Logger using the Aspect concepts of CThru, which I call as follows:
1// set the name of the types I want to monitor
TestProject.LoggingAspect.TypeNamesToMatch.Add("SharePoint");
1// tell it where to look for aspects
CThru.CThruEngine.AddAspectsInAssembly(System.Reflection.Assembly.GetExecutingAssembly());
1// and start it up
CThru.CThruEngine.StartListening();
1
2The source is below is just included in my assembly, it allow me to chose if I want to log as text for CSV format. I am sure it will need editing for your logging needs but it gives you the basic idea….
using System;
1using CThru;
using System.Diagnostics;
1using System.Collections.Generic;
using System.Text;
namespace TestProject
1{
/// <summary>
1 /// A sample Aspect logger for CThru
/// </summary>
1 class LoggingAspect : Aspect
{
1 /// <summary>
/// The current logger in use
1 /// </summary>
private static IAspectLogger logger = new DebugTextLogger() ;
/// <summary>
1 /// A list of the available logging formats
/// </summary>
1 public enum LoggingMethod
{
1 TextToDebug,
CommaSeparatedToDebug
1 }
2```
3
4```
5 /// <summary>
/// The list of string to do partial matches against when logging
1 /// If any string in this list is in the namespace or typename it gets logged
/// If this list is empty then all types are logged
1 /// </summary>
public static List<string\> TypeNamesToMatch = new List<string\>();
/// <summary>
1 /// Sets the current logging format
/// </summary>
1 public static LoggingMethod CurrentLoggingMethod
{
1 set
{
1 switch (value)
{
1 default:
case LoggingMethod.TextToDebug:
1 logger = new DebugTextLogger();
break;
1 case LoggingMethod.CommaSeparatedToDebug:
logger = new DebugCVSLogger();
1 break;
}
}
}
public static bool LogToCSV = false;
public override void StaticConstructorBehavior(DuringCallbackEventArgs e)
1 {
LogEvent("LoggingAspect.StaticConstructorBehavior", e);
1 }
2```
3
4```
5 public override void ConstructorBehavior(DuringCallbackEventArgs e)
{
1 LogEvent("LoggingAspect.ConstructorBehavior", e);
}
public override void MethodBehavior(DuringCallbackEventArgs e)
1 {
LogEvent("LoggingAspect.MethodBehavior", e);
if (e.MethodName == "StsCompareStrings")
1 {
e.MethodBehavior = MethodBehaviors.ReturnsCustomValue;
1 e.ReturnValueOrException = true;
}
}
public override void MissingMethodBehavior(DuringCallbackEventArgs e)
1 {
LogEvent("LoggingAspect.MissingMethodBehavior", e);
1 }
2```
3
4```
5 private static void LogEvent(string description, DuringCallbackEventArgs e)
{
1 logger.LogEvent(description, e);
}
/// <summary>
1 /// The control to see which
/// </summary>
1 /// <param name="info">The info on the currently handled assembly</param>
/// <returns>True if we should monitor this event</returns>
1 public override bool ShouldIntercept(InterceptInfo info)
{
1 if (TypeNamesToMatch.Count > 0)
{
1 foreach (string name in TypeNamesToMatch)
{
1 // find the first match of this string in a namespace typename
if (info.TypeName.Contains(name) == true)
1 {
return true;
1 }
}
1 }
else
1 {
// none in the list match all
1 return true;
}
1 return false;
}
/// <summary>
1 /// Helper method to format the parameters as a list in a string
/// </summary>
1 /// <param name="e">The handled event</param>
/// <returns>A strung listing the params and their values</returns>
1 public static string ParametersListToString(DuringCallbackEventArgs e)
{
1 var sb = new StringBuilder();
if (e.ParameterValues != null)
1 {
for (int i = 0; i < e.ParameterValues.Length; i++)
1 {
2```
3
4```
5 if (e.ParameterValues\[i\] != null)
{
1 sb.Append(String.Format("{0} \[{1}\]", e.ParameterValues\[i\].GetType(), e.ParameterValues\[i\]));
}
1 else
{
1 sb.Append("null");
}
1 if (i < e.ParameterValues.Length - 1)
{
1 sb.Append(",");
}
1 }
}
1 return sb.ToString();
}
}
/// <summary>
1 /// Logger interface
/// </summary>
1 public interface IAspectLogger
{
1 void LogEvent(string description, DuringCallbackEventArgs e);
}
/// <summary>
1 /// Logs an items as plain text
/// </summary>
1 public class DebugTextLogger : IAspectLogger
{
1 public void LogEvent(string description, DuringCallbackEventArgs e)
{
1 Debug.WriteLine(string.Format("{0}: {1}{2}.{3}({4})",
description,
1 e.TargetInstance == null ? "\[Static\] " : string.Empty,
e.TypeName,
1 e.MethodName,
LoggingAspect.ParametersListToString(e)));
1 }
}
/// <summary>
1 /// Logs an items as comma separated to ease analysis
/// </summary>
1 public class DebugCVSLogger : IAspectLogger
{
public DebugCVSLogger()
1 {
// write out a header so we know the colomns
1 Debug.WriteLine(string.Format("{0},{1},{2},{3},{4}",
"Event logged",
1 "Is Static",
"Type name",
1 "Method name",
"Parameter List...."));
1 }
2```
3
4```
5 public void LogEvent(string description, DuringCallbackEventArgs e)
{
1 Debug.WriteLine(string.Format("{0},{1},{2},{3},{4}",
description,
1 e.TargetInstance == null ? "True" : "False",
e.TypeName,
1 e.MethodName,
LoggingAspect.ParametersListToString(e)));
1 }
}
}