Git Product home page Git Product logo

audit.net's Introduction

Audit.NET

Gitter     fede

An extensible framework to audit executing operations in .NET including support for .NET Framework ≥ 4.5 and NetCore ≥ 1.0 (NetStandard 1.3).

Generate an audit log with evidence for reconstruction and examination of activities that have affected specific operations or procedures.

With Audit.NET you can generate tracking information about operations being executed. It will log environmental information such as the caller user id, machine name, method name, exceptions, including execution time and duration, and exposing an extensible mechanism in which you can provide extra information or implement your output mechanism for the audit logs.

Extensions to log to json Files, Event Log, SQL, MySQL, MongoDB, AzureBlob, DocumentDB and Redis are provided. And also extensions to audit different systems such as Entity Framework, MVC, WebAPI and WCF.

NuGet Status

To install the package run the following command on the Package Manager Console:

PM> Install-Package Audit.NET

Contents

Usage

Create an Audit Scope by calling the static AuditScope.Create method.

Suppose you have the following code to cancel an order that you want to audit:

Order order = Db.GetOrder(orderId);
order.Status = -1;
order.OrderItems = null;
order = Db.OrderUpdate(order);

To audit this operation, you can surround the code with a using block that creates an AuditScope, indicating a target object to track:

Order order = Db.GetOrder(orderId);
using (AuditScope.Create("Order:Update", () => order))
{
    order.Status = -1;
    order.OrderItems = null;
    order = Db.OrderUpdate(order);
}

It is not mandatory to use a using block, but it simplifies the syntax when the code to audit is on a single block, allowing to detect exceptions and calculate the duration by implicitly saving the event on disposal.

The first parameter of the Create method is an event type name intended to identify and group the events. The second is the delegate to obtain the object to track (target object). This object is passed as a Func<object> to allow the library inspect the value at the beggining and at the disposal of the scope. It is not mandatory to supply a target object, pass null when you don't want to track a specific object.

There is also a unified overload of the Create method that accepts an instance of AuditScopeOptions. Use this class to configure any of the available options for the scope:

var options = new AuditScopeOptions()
{
	EventType = "MyEvent",
	CreationPolicy = EventCreationPolicy.Manual,
	ExtraFields = new { Action = this.Action },
	AuditEvent = new MyCustomAuditEvent()
};
using (var scope = AuditScope.Create(options))
{
	// ...
}

Simple logging

If you are not tracking an object, nor the duration of an event, you can use the CreateAndSave shortcut method that logs an event immediately. For example:

AuditScope.CreateAndSave("Event Type", new { ExtraField = "extra value" });

Manual Saving

You can control the creation and saving logic, by creating a manual AuditScope. For example to log a pair of Start/End method calls as a single event:

public class SomethingThatStartsAndEnds
{
    private AuditScope auditScope;

    public int Status { get; set; }

    public void Start()
    {
        // Create a manual scope
        auditScope = AuditScope.Create("MyEvent", () => Status, EventCreationPolicy.Manual);
    }

    public void End()
    {
        // Save the event
        auditScope.Save();  
        // Discard to avoid further saving
        auditScope.Discard();
    }
}

For more information about the EventCreationPolicy please see Event Creation Policy section.

Output

The library will generate an output (AuditEvent) for each operation, including:

  • Tracked object's state before and after the operation.
  • Execution time and duration.
  • Environment information such as user, machine, domain, locale, etc.
  • Comments and Custom Fields provided.

An example of the output in JSON:

{
	"EventType": "Order:Update",
	"Environment": {
		"UserName": "Federico",
		"MachineName": "HP",
		"DomainName": "HP",
		"CallingMethodName": "Audit.UnitTest.AuditTests.TestUpdate()",
		"Exception": null,
		"Culture": "en-GB"
	},
	"StartDate": "2016-08-23T11:33:14.653191-05:00",
	"EndDate": "2016-08-23T11:33:23.1820786-05:00",
	"Duration": 8529,
	"Target": {
		"Type": "Order",
		"Old": {
			"OrderId": "39dc0d86-d5fc-4d2e-b918-fb1a97710c99",
			"Status": 2,
			"OrderItems": [{
				"Sku": "1002",
				"Quantity": 3.0
			}]
		},
		"New": {
			"OrderId": "39dc0d86-d5fc-4d2e-b918-fb1a97710c99",
			"Status": -1,
			"OrderItems": null
		}
	}
}

Output details

The following tables describes the output fields:

Field Name Type Description
EventType string User-defined string to group the events
Environment Environment Contains information about the execution environment
StartDate DateTime Date and time when the event has started
EndDate DateTime Date and time when the event has ended
Duration integer Duration of the event in milliseconds
Target Target User-defined tracked object
Comments Array of strings User-defined comments
CustomFields Dictionary User-defined custom fields
Field Name Type Description
UserName string Current logged user name
MachineName string Executing machine name
DomainName string Current user domain
CallingMethodName string Calling method signature information
Exception string Indicates if an Exception has been detected (NULL if no exception has been thrown)
Culture string Current culture identifier
Field Name Type Description
Type string Tracked object type name
Old Object Value of the tracked object at the beginning of the event
New Object Value of the tracked object at the end of the event

Custom Fields and Comments

The AuditScope object provides two methods to extend the event output.

  • Use SetCustomField() method to add any object as a custom field of the event.

  • Use Comment() to add textual comments to the event.

For example:

Order order = Db.GetOrder(orderId);
using (var audit = AuditScope.Create("Order:Update", () => order))
{
    audit.SetCustomField("ReferenceId", orderId);
    order.Status = -1;
    order = Db.OrderUpdate(order);
    audit.Comment("Status Updated to Cancelled");
}

You can also set Custom Fields when creating the AuditScope, by passing an anonymous object with the properties you want as extra fields. For example:

using (var audit = AuditScope.Create("Order:Update", () => order, new { ReferenceId = orderId }))
{
    order.Status = -1;
    order = Db.OrderUpdate(order);
    audit.Comment("Status Updated to Cancelled");
}

You can also access the Custom Fields directly from Event.CustomFields property of the scope. For example:

using (var audit = AuditScope.Create("Order:Update", () => order, new { ReferenceId = orderId }))
{
    audit.Event.CustomFields["ReferenceId"] = orderId;
}

Custom fields are not limited to single properties, you can store any object as well, by default they will be JSON serialized.

The output of the previous examples would be:

{
	"EventType": "Order:Update",
	"Environment": {
		"UserName": "Federico",
		"MachineName": "HP",
		"DomainName": "HP",
		"CallingMethodName": "Audit.UnitTest.AuditTests.TestUpdate()",
		"Exception": null,
		"Culture": "en-GB"
	},
	"Target": {
		"Type": "Order",
		"Old": {
			"OrderId": "39dc0d86-d5fc-4d2e-b918-fb1a97710c99",
			"Status": 2,
			
		},
		"New": {
			"OrderId": "39dc0d86-d5fc-4d2e-b918-fb1a97710c99",
			"Status": -1,
			
		}
	},
	"ReferenceId": "39dc0d86-d5fc-4d2e-b918-fb1a97710c99",           // <-- Custom Field
	"Comments": ["Status Updated to Cancelled"],                     // <-- Comments
	"StartDate": "2016-08-23T11:34:44.656101-05:00",
	"EndDate": "2016-08-23T11:34:55.1810821-05:00",
	"Duration": 8531
}

Discard option

The AuditScope object has a Discard() method to allow the user to discard an event under certain condition.

For example, if you want to avoid saving the audit event when an exception is thrown:

using (var scope = AuditScope.Create("SomeEvent", () => someTarget))
{
    try
    {
        //some operation
        Critical.Operation();
    }
    catch (Exception ex)
    {
        //If an exception is thown, discard the audit event
        scope.Discard();
    }
}

Data providers

A data provider (or storage provider) contains the logic to handle the audit event output, where you define what to do with the audit logs.

You can use one of the data providers included or inject your own mechanism by creating a class that inherits from AuditDataProvider, overriding the following methods:

  • InsertEvent: should return a unique ID for the event.
  • ReplaceEvent: should update an event given its ID, this method is only called for Creation Policies Manual or InsertOnStartReplaceOnEnd.

For example:

public class MyCustomDataProvider : AuditDataProvider
{
    public override object InsertEvent(AuditEvent auditEvent)
    {
        // AuditEvent provides a ToJson() method
        string json = auditEvent.ToJson();
        // Write the json representation of the event to a randomly named file
        var fileName = "Log" + Guid.NewGuid().ToString() + ".json";
        File.WriteAllText(fileName, json);
        return fileName;
    }
    // Replaces an existing event given the ID and the event
    public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
    {
        // Override an existing event
        var fileName = eventId.ToString();
        File.WriteAllText(fileName, auditEvent.ToJson());
    }
}

You can set a default data provider assigning the DataProvider property on the global Configuration object. For example:

Audit.Core.Configuration.DataProvider = new MyCustomDataProvider();

Or using the fluent API:

Audit.Core.Configuration.Setup()
	.UseCustomProvider(new MyCustomDataProvider());

See Configuration section for more information.

You can also set the data provider per-scope, by using an appropriate overload of the AuditScope.Create method. For example:

AuditScope.Create("Order:Update", () => order, EventCreationPolicy.Manual, new MyCustomDataProvider());

As an anternative to creating your own data provider class, you can define the mechanism at run time by using the DynamicDataProvider class. For example:

var dataProvider = new DynamicDataProvider();
// Attach an action for insert
dataProvider.AttachOnInsert(ev => Console.Write(ev.ToJson()));
Audit.Core.Configuration.DataProvider = dataProvider;

Or by using the fluent API:

Audit.Core.Configuration.Setup()
	.UseDynamicProvider(config => config
		.OnInsert(ev => Console.Write(ev.ToJson())));

Data providers included

The Data Providers included are summarized in the following table:

Data Provider Package Description Configuration API
FileDataProvider Audit.NET Store the audit logs as files. Dynamically configure the directory and path. .UseFileLogProvider()
EventLogDataProvider Audit.NET Write the audit logs to the Windows EventLog. .UseEventLogProvider()
DynamicDataProvider Audit.NET Dynamically change the behavior at run-time. Define Insert and a Replace actions with lambda expressions. .UseDynamicProvider()
SqlDataProvider Audit.NET.SqlServer Store the events as rows in a MS SQL Table, in JSON format. .UseSqlServer()
MySqlDataProvider Audit.NET.MySql Store the events as rows in a MySQL database table, in JSON format. .UseMySql()
MongoDataProvider Audit.NET.MongoDB Store the events in a Mongo DB collection, in BSON format. .UseMongoDB()
AzureDbDataProvider Audit.NET.AzureDocumentDB Store the events in an Azure Document DB collection, in JSON format. .UseAzureDocumentDB()
AzureBlobDataProvider Audit.NET.AzureStorage Store the events in an Azure Blob Storage container, in JSON format. .UseAzureBlobStorage()
UdpDataProvider Audit.NET.Udp Send Audit Logs as UDP datagrams to a network. .UseUdp()
RedisDataProvider Audit.NET.Redis Store audit logs in Redis as Strings, Lists, SortedSets, Hashes or publish to a PubSub channel. .UseRedis()

Event Creation Policy

The audit scope can be configured to call its data provider in different ways:

  • Insert on End: (default) The audit event is inserted when the scope is disposed.

  • Insert on Start, Replace on End: The event (on its initial state) is inserted when the scope is created, and then the complete event information is replaced when the scope is disposed.

  • Insert on Start, Insert on End: Two versions of the event are inserted, the initial when the scope is created, and the final when the scope is disposed.

  • Manual: The event saving (insert/replace) should be explicitly invoked by calling the Save() method on the AuditScope.

You can set the Creation Policy per-scope, for example to explicitly set the Creation Policy to Manual:

using (var scope = AuditScope.Create("MyEvent", () => target, EventCreationPolicy.Manual))
{
    //...
    scope.Save();
}

If you don't provide a Creation Policy, the default Creation Policy configured will be used (see next section).

Configuration

Data provider

To change the default data provider, set the static property DataProvider on Audit.Core.Configuration class. This should be done prior to the AuditScope creation, i.e. during application startup.

For example, to set your own provider as the default data provider:

Audit.Core.Configuration.DataProvider = new MyCustomDataProvider();

If you don't specify a Data Provider, a default FileDataProvider will be used to write the events as .json files into the current working directory.

Creation Policy

To change the default creation policy, set the static property SetCreationPolicy on Audit.Core.Configuration class. This should be done prior to the AuditScope creation, i.e. during application startup.

For example, to set the default creation policy to Manual:

Audit.Core.Configuration.CreationPolicy = EventCreationPolicy.Manual;

If you don't specify a Creation Policy, the default Insert on End will be used.

Custom Actions

You can configure Custom Actions that are executed for all the Audit Scopes in your application. This allows to globally change the behavior and data, intercepting the scopes after they are created or before they are saved.

Call the static AddCustomAction() method on Audit.Core.Configuration class to attach a custom action.

For example, to globally discard the events under centain condition:

Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
{
    if (DateTime.Now.Hour == 17) // Tea time
    {
        scope.Discard();
    }
});

Or to add custom fields / comments globally to all scopes:

Audit.Core.Configuration.AddCustomAction(ActionType.OnEventSaving, scope =>
{
    if (scope.Event.Environment.Exception != null)
    {
        scope.SetCustomField("Oops", true);
    }
    scope.Comment("Saved at " + DateTime.Now);
});

The ActionType indicates when to perform the action. The allowed values are:

  • OnScopeCreated: When the Audit Scope is being created, before any saving. This is executed once per Audit Scope.
  • OnEventSaving: When an Audit Scope's Event is about to be saved.

Configuration Fluent API

Alternatively to the properties/methods mentioned before, you can configure the library using a convenient Fluent API provided by the method Audit.Core.Configuration.Setup(), this is the most straightforward way to configure the library.

For example, to set the FileLog Provider with its default settings using a Manual creation policy:

Audit.Core.Configuration.Setup()
    .UseFileLogProvider()
    .WithCreationPolicy(EventCreationPolicy.Manual);

Configuration examples

File log provider with dynamic directory path and filename:
Audit.Core.Configuration.Setup()
    .UseFileLogProvider(config => config
        .DirectoryBuilder(_ => $@"C:\Logs\{DateTime.Now:yyyy-MM-dd}")
        .FilenameBuilder(auditEvent => $"{auditEvent.Environment.UserName}_{DateTime.Now.Ticks}.json"));
File log provider with an InsertOnStart-ReplaceOnEnd creation policy, and a global custom field set in a custom action:
Audit.Core.Configuration.Setup()
    .UseFileLogProvider(config => config
        .FilenamePrefix("Event_")
        .Directory(@"C:\AuditLogs\1"))
    .WithCreationPolicy(EventCreationPolicy.InsertOnStartReplaceOnEnd)
    .WithAction(x => x.OnScopeCreated(scope => scope.SetCustomField("ApplicationId", "MyApplication")));
Event log provider with an InsertOnEnd creation policy:
Audit.Core.Configuration.Setup()
    .UseEventLogProvider(config => config
        .SourcePath("My Audited Application")
        .LogName("Application"))
    .WithCreationPolicy(EventCreationPolicy.InsertOnEnd);
Dynamic provider to log to the console:
Audit.Core.Configuration.Setup()
    .UseDynamicProvider(config => config
        .OnInsert(ev => Console.WriteLine("{0}: {1}->{2}", ev.StartDate, ev.Environment.UserName, ev.EventType)));

Extensions

The following packages are extensions to log interactions with different systems such as MVC, WebApi, WCF and Entity Framework:

Audit.NET Extensions

Generate detailed server-side audit logs for Windows Communication Foundation (WCF) service calls, by configuring a provided behavior.

Generate detailed audit logs for CRUD operations on Entity Framework, by inheriting from a provided DbContext or IdentityDbContext. Includes support for EF 6 and EF 7 (EF Core).

Generate detailed audit logs by decorating Web API Methods and Controllers with an action filter attribute. Includes support for ASP.NET Core.

Generate detailed audit logs by decorating MVC Actions and Controllers with an action filter attribute. Includes support for ASP.NET Core MVC.

Generate detailed audit logs for any class without changing its code by using a proxy.

Storage providers

Apart from the FileLog, EventLog and Dynamic event storage providers, there are others included in different packages:

Storage providers

Store the events as rows in a SQL Table, in JSON format.

Store the events as rows in MySQL database, in JSON format.

Store the events in a Mongo DB Collection, in BSON format.

Store the events in an Azure Document DB Collection, in JSON format.

Store the events in an Azure Blob Storage container, in JSON format.

Send Audit Logs as UDP datagrams to a network.

Store Audit Logs in a Redis database as String, List, Hash, Sorted Set or publishing to a Redis PubSub channel.

audit.net's People

Contributors

thepirat000 avatar

Watchers

James Cloos avatar Marcello Drewanz avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.