Post­Sharp Documentation / Logging / Adding Audit to your Solution

Adding Audit to your Solution

Auditing and logging are technically very similar but serve different purposes. Whereas application logs are typically used by server administrators or technical support engineers to diagnose technical issues, audit trails are primarily used by security analysts and business users.

A typical business application will need to implement both logging and audit.

This article explains how to add audit into your application and how to customize the default behaviors.

This topic contains the following sections:

Adding audit to your projects

To audit your code, you have to add the AuditAttribute attribute to all methods to audit, and implement a handler for the AuditServices.RecordPublished event event. Let's see that in details.

To add audit to your application:

  1. Add a reference to the PostSharp.Patterns.Diagnostics package to your project.

  2. Decide how you want to tag methods that need to be audited. If you want to select them individually, add the AuditAttribute attribute to each of them. For instance:

    C#
    [Audit]
    public void AssignTo(Employee employee)
    {
    }

    If you need to audit dozens or hundreds of methods, you can select them using project-wide matching rule. Create a source code file where you will add all project-wise aspects. We suggest naming this file GlobalAspects.cs. Then add the following content to this file:

    C#
    using PostSharp.Patterns.Diagnostics;
    using PostSharp.Extensibility;
    
    [assembly: Audit(AttributePriority = 1, AttribueTargetTypes = "Contoso.BusinessObjects.*", AttributeTargetMemberAttributes = MulticastAttributes.Public)]
    [assembly: Audit(AttributePriority = 2, AttributeExclude = true, AttributeTargetMembers = "get_*" )]

    This code adds audit to all public methods except property getters. You can edit this code to target methods relevant to your scenarios. See Adding Aspects to Code for details.

  3. Implement the code that appends the audit record into your database table. This code needs to react to the AuditServices.RecordPublished event.

    C#
    AuditServices.RecordPublished += delegate(object o, AuditRecordEventArgs e)
        {
            var record = new DbAuditRecord(
                WindowsIdentity.GetCurrent().Name,
                ((BusinessObject)e.Record.Target).Id,
                e.Record.MemberName,
                e.Record.Text
            );
    
            record.AppendToDatabase();
        };
Adding more information to the audit record

The AuditRecord class exposed by the AuditServices.RecordPublished event only contain the most common pieces of information regarding the audited operation. If you want to include more information in the record event, you need to override the audit back-end.

Since the audit back-end is simply another logging back-end, you can use the same approach as described in Implementing an Adapter to a Custom Logging Framework. Here are the specific steps you need to follow to add more information to the AuditRecord class class.

To extend the audit record object:

  1. Create a new class derived from AuditRecord add all necesarry fields or properties.

    C#
    using System;
    using System.Collections.Generic;
    using PostSharp.Patterns.Diagnostics;
    using PostSharp.Patterns.Diagnostics.Audit;
    
    namespace PostSharp.Samples.Audit.Extended
    {
        public class ExtendedAuditRecord : AuditRecord
        {
            public List<BusinessObject> RelatedBusinessObjects { get; } = new List<BusinessObject>();
    
            public ExtendedAuditRecord(Type declaringType, string memberName, LogRecordKind recordKind) : base(declaringType, memberName, recordKind)
            {
            }
        }
    }
  2. Create a new class derived from AuditRecordBuilder.

    Override the CreateRecord(LoggingContext, LogRecordInfo, LogMemberInfo) method and return a instance of your new custom AuditRecord class. The instance you return here will be stored in the AuditRecordBuilder.CurrentRecord property.

    Then override other methods such as SetParameter<T>(Int32, String, ParameterDirection, String, T, IFormatter<T>) and assign the custom fields of properties of the CurrentRecord property.

    C#
    using PostSharp.Patterns.Diagnostics.Audit;
    using PostSharp.Patterns.Diagnostics.Backends.Audit;
    using PostSharp.Patterns.Diagnostics.Contexts;
    using PostSharp.Patterns.Diagnostics.RecordBuilders;
    using PostSharp.Patterns.Formatters;
    
    namespace PostSharp.Samples.Audit.Extended
    {
        public class ExtendedAuditRecordBuilder : AuditRecordBuilder
        {
            public ExtendedAuditRecordBuilder(AuditBackend backend) : base(backend)
            {
            }
    
            protected override AuditRecord CreateRecord(LoggingContext context, ref LogRecordInfo recordInfo, ref LogMemberInfo memberInfo)
            {
                return new ExtendedAuditRecord(context.Source.SourceType, memberInfo.MemberName, recordInfo.RecordKind);
            }
    
            public override void SetParameter<T>(int index, string parameterName, ParameterDirection direction, string typeName, T value,
                IFormatter<T> formatter)
            {
                var businessObject = value as BusinessObject;
    
                if (businessObject != null)
                {
                    ((ExtendedAuditRecord) this.CurrentRecord).RelatedBusinessObjects.Add(businessObject);
                }
    
                base.SetParameter(index, parameterName, direction, typeName, value, formatter);
            }
        }
    }
  3. Create a new class derived from AuditBackend. Override the CreateRecordBuilder() method and return a new instance of your custom AuditRecordBuilder.

    C#
    using PostSharp.Patterns.Diagnostics.Backends.Audit;
    using PostSharp.Patterns.Diagnostics.RecordBuilders;
    
    namespace PostSharp.Samples.Audit.Extended
    {
        public class ExtendedAuditBackend : AuditBackend
        {
            public override LogRecordBuilder CreateRecordBuilder()
            {
                return new ExtendedAuditRecordBuilder(this);
            }
        }
    }
  4. Set an instance of new class as the logging back-end for the Audit role:

    C#
    LoggingServices.Roles[LoggingRoles.Audit].Backend = new ExtendedAuditBackend();
See Also