To create a custom trace listner for the Enterprise Library you need to derive from CustomTraceListener. You will then need to implement the TraceData function.
[ConfigurationElementType(typeof(CustomTraceListenerData))]
public class DBAuditLoggerTraceListener :
Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.CustomTraceListener
{
public override void Write(string message)
{
throw new NotImplementedException("This method is not supported");
}
public override void WriteLine(string message)
{
throw new NotImplementedException("This method is not supported");
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
AuditLogEntry ale = data as AuditLogEntry;
if (ale == null)
{
//Not an audit log entry
return;
}
IAuditLoggerExpressionGroup logGroup;
if (!AuditLoggerExpressionCache.Instance.TryGetValue(ale.AuditCategory, out logGroup))
{
//Category not registered
return;
}
using (StoredProcedure sp = new StoredProcedure(logGroup.Procedure))
{
foreach (string key in logGroup.Keys)
{
sp.AddParameter(key, logGroup.GetValue(key, ale));
}
sp.Execute();
}
}
A couple of things to note, The Stored Procedure class is my own class which I am unable to share; however, all it does is wrap up all the ADO.Net Goodness. It also handles my connection strings which is why I am not passing the connection string in. You should be able to quickly whip up your own ADO.Net code. If you run into issues let me know and I will be glad to help out.
Basically all we are doing is making sure we have an AuditLogEntry object, then we check if we have created an ExpressionGroup for this AuditCategory. If we have we simply loop through each of the keys in the ExpressionGroup, calling the GetValue method.
GetValue was needed to ensure that this class didn't have to know about the type parameter in the Expression Group. I'm hopping C#4 with co/contra variance will remove this requirement. Finally we execute the stored procedure.
The next step is to configure this. If your using an ASP.Net app I'd recommend that you configure your expression Groups in the Global.asax. Also remember to call the Cache() method. If you forget this you will not save the expression group you have created.
The final step it to add your logger to the EntLib configuration for example:
<loggingConfiguration name="Logging Application Block" tracingEnabled="true" defaultCategory="Error" logWarningsWhenNoCategoriesMatch="true">
<listeners>
<add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0"
traceOutputOptions="None" type="DBAuditLoggerTraceListener, YourAssembly" name="AuditLogger" initializeData="" formatter="Text Formatter"/>
</listeners>
<formatters>
<add template="Timestamp: {timestamp}
"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging"
name="Text Formatter"/>
</formatters>
<categorySources>
<add switchValue="All" name="Audit">
<listeners>
<add name="AuditLogger"/>
</listeners>
</add>
</categorySources>
</loggingConfiguration>
I just noticed I had left the text formatter in there which I have not used. I am not sure if this is required or not.
So after all of this what do we have? A logger that will take any AuditLogEntry and execute a stored procedure, with any number of parameters. I hope you find this useful. Please feel free to provide any feedback positive or negative.
Enjoy!
Update
I have extracted the source and posted online. Please note that this is not tested, so if you run into any issues let me know. When I get caught back up with my day job I will try and finish rounding this out.
Let me know if this link doesn't work. First time I've used skydrive so I'm not positive I have it all setup right.