Coder Social home page Coder Social logo

thepirat000 / audit.net Goto Github PK

View Code? Open in Web Editor NEW
2.2K 81.0 320.0 4.96 MB

An extensible framework to audit executing operations in .NET and .NET Core.

License: MIT License

C# 98.13% PowerShell 0.27% Batchfile 0.72% CSS 0.04% JavaScript 0.01% HTML 0.57% TSQL 0.27%
audit-log audit entity-framework wcf mvc webapi netcore audit-logs

audit.net's Introduction

Audit.NET

USAGE | OUTPUT | CUSTOMIZATION | DATA PROVIDERS | CREATION POLICY | CONFIGURATION | EXTENSIONS

issues build status chat / support donations
issues-openissues-closed build-status Gitter Gitter backers paypal

An extensible framework to audit executing operations in .NET and .NET Core.

Generate audit logs 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 gathers environmental information such as the caller user ID, machine name, method name, and exceptions, including execution time and exposing an extensible mechanism to enrich the logs and handle the audit output.

NuGet Status NuGet Count

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

PM> Install-Package Audit.NET

Changelog

Check the CHANGELOG.md file.

Introduction

The Audit Scope and Audit Event are the central objects of this framework.

Audit Scope

The AuditScope serves as the central object in this framework, representing the scope of an audited operation or event. It acts as a context for auditing, capturing pertinent details like the start time, involved entities, and any additional custom information. Essentially, the AuditScope encapsulates an AuditEvent, controlling its life cycle.

The AuditScope is a disposable object, commonly utilized within a using statement to ensure proper finalization and recording of audit information upon exiting the scope.

See the audit scope statechart.

Audit Event

The AuditEvent functions as an extensible information container that captures the details of the audited operation, is the representation of the audited information within an Audit Scope. It includes details about the audited operation, such as the event type, timestamp, execution duration, and any custom fields or properties.

The AuditEvent is typically serialized into a format suitable for storage or transmission, such as JSON.

The audit events are stored using a Data Provider. You can use one of the available data providers or implement your own.

SUPPORT FOR OLDER .NET FRAMEWORKS

Beginning with version 23.0.0, this library and its extensions have discontinued support for older .NET Framework and Entity Framework (versions that lost Microsoft support before 2023).

For reference, please consult the following links:

This library and its extensions will maintain support for the following minimum .NET framework versions:

  • .NET Framework 4.6.2 (net462)
  • .NET Standard 2.0 (netstandard2.0)
  • .NET 6 (net6.0)

The following frameworks were deprecated and removed from the list of target frameworks:

  • net45, net451, net452, net461
  • netstandard1.3, netstandard1.4, netstandard1.5, netstandard1.6
  • netcoreapp2.1, netcoreapp3.0
  • net5.0

This discontinuation led to the following modifications:

  • All library versions will now use System.Text.Json as the default (Newtonsoft.Json will be deprecated but can still be used through the JsonAdapter).
  • Support for EF Core versions 3 and earlier has been discontinued in the Audit.EntityFramework.Core libraries. The minimum supported version is now EF Core 5 (Audit.EntityFramework will continue to support .NET Entity Framework 6).
  • The libraries Audit.EntityFramework.Core.v3 and Audit.EntityFramework.Identity.Core.v3 has been deprecated.
  • Audit.NET.JsonSystemAdapter has been deprecated.

Usage

The Audit Scope is the central object of this framework. It encapsulates an audit event, controlling its life cycle. The Audit Event is an extensible information container of an audited operation.

There are several ways to create an Audit Scope:

  • Calling the Create() / CreateAsync() method of an AuditScopeFactory instance, for example:

    var factory = new AuditScopeFactory();
    var scope = factory.Create(new AuditScopeOptions(...));
  • Using the overloads of the static methods Create() / CreateAsync() on AuditScope, for example:

    var scope = AuditScope.Create("Order:Update", () => order, new { MyProperty = "value" });

    The first parameter of the AuditScope.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 to inspect the value at the beginning and the disposal of the scope. It is not mandatory to supply a target object.

    You can use the overload that accepts an AuditScopeOptions instance to configure any of the available options for the scope:

    var scope = AuditScope.Create(new AuditScopeOptions()
    {
      EventType = "Order:Update",
      TargetGetter = () => order,
      ExtraFields = new { MyProperty = "value" }
    });
  • Using the provided fluent API, for example:

    var scope = AuditScope.Create(_ => _
        .EventType("Order:Update")
        .ExtraFields(new { MyProperty = "value" })
        .Target(() => order));

AuditScope options

Option Type Description
EventType string A string representing the type of the event
TargetGetter Func<object> Target object getter (a func that returns the object to track)
ExtraFields object Anonymous object that contains additional fields to be merged into the audit event
DataProvider AuditDataProvider The data provider to use. Defaults to the DataProvider configured on Audit.Core.Configuration.DataProvider
CreationPolicy EventCreationPolicy The creation policy to use. Default is InsertOnEnd
IsCreateAndSave bool Value indicating whether this scope should be immediately ended and saved after creation. Default is false
AuditEvent AuditEvent Custom initial audit event to use. By default it will create a new instance of basic AuditEvent
SkipExtraFrames int Value used to indicate how many frames in the stack should be skipped to determine the calling method. Default is 0
CallingMethod MethodBase Specific calling method to store on the event. Default is to use the calling stack to determine the calling 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);
}

Note

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 the detection of exceptions and calculating the duration by implicitly saving the event on disposal.

Note

When using the extensions that logs interactions with different systems, like Audit.EntityFramework, Audit.WebApi, etc. you don't need to explicitly create the AuditScope or AuditEvent, they are created internally by the extension.

Simple logging

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

AuditScope.Log("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(new AuditScopeOptions()
        {
            EventType = "MyEvent",
            TargetGetter = () => this.Status,
            CreationPolicy = 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"
    },
    "Activity": {
        "StartTimeUtc": "2023-12-01T17:36:52.2256288Z",
	"SpanId": "23a93b9e8cbc457f",
	"TraceId": "2d3e5e90f790c7d2274d9bb047531f66",
	"ParentId": "0000000000000000",
	"Operation": "Update"
    },
    "StartDate": "2016-08-23T11:33:14.653191Z",
    "EndDate": "2016-08-23T11:33:23.1820786Z",
    "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 describe 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
StackTrace string The full stack trace at the moment of the audit scope creation (NULL unless it's enabled by configuration)
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 an extra field to the event.
  • Use Comment() to add textual comments to the event's Comments array.

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;
}

Note

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

Extending AuditEvent

Another way to enrich the event output is to create a class inheriting from the AuditEvent class, then you can pass an instance of your class to the AuditScope.Create method. For example:

public class YourAuditEvent : AuditEvent
{
    public Guid ReferenceId { get; set; } = Guid.NewGuid();
}

using (var scope = AuditScope.Create(new AuditScopeOptions { AuditEvent = new YourAuditEvent() }))
{
    //...
}

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. Discarding an event means it won't be saved.

For example, if you want to avoid saving the audit event under certain conditions:

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

Data providers

A data provider (or storage sink) 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 and overrides its methods:

  • InsertEvent: should store the event and return a unique ID.
  • ReplaceEvent: should update an event given its ID. This method is only used for Creation Policies Manual or InsertOnStartReplaceOnEnd.

If your data provider will support asynchronous operations, you must also implement the following methods:

  • InsertEventAsync: Asynchronous implementation of the InsertEvent method.
  • ReplaceEventAsync: Asynchronous implementation of the ReplaceEvent method.

Also, if your data provider will support event retrieval, you should implement the following methods:

  • GetEvent: Retrieves an event by id.
  • GetEventAsync: Asynchronous implementation of the GetEvent method.

For example:

public class MyCustomDataProvider : AuditDataProvider
{
    public override object InsertEvent(AuditEvent auditEvent)
    {
        var fileName = $"Log{Guid.NewGuid()}.json";
        File.WriteAllText(fileName, auditEvent.ToJson());
        return fileName;
    }
    public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
    {
        var fileName = eventId.ToString();
        File.WriteAllText(fileName, auditEvent.ToJson());
    }
    public override T GetEvent<T>(object eventId)
    {
        var fileName = eventId.ToString();
        return JsonConvert.DeserializeObject<T>(File.ReadAllText(fileName));
    }
    // async implementation:
    public override async Task<object> InsertEventAsync(AuditEvent auditEvent, CancellationToken cancellationToken = default)
    {
        var fileName = $"Log{Guid.NewGuid()}.json";
        await File.WriteAllTextAsync(fileName, auditEvent.ToJson(), cancellationToken);
        return fileName;
    }
    public override async Task ReplaceEventAsync(object eventId, AuditEvent auditEvent, CancellationToken cancellationToken = default)
    {
        var fileName = eventId.ToString();
        await File.WriteAllTextAsync(fileName, auditEvent.ToJson(), cancellationToken);
    }
    public override async Task<T> GetEventAsync<T>(object eventId, CancellationToken cancellationToken = default)
    {
        var fileName = eventId.ToString();
        return await GetFromFileAsync<T>(cancellationToken);
    }
}

Data provider selection

The data provider can be set globally for the entire application or per audit scope.

Note

If you don't specify a global data provider, it will default to a FileDataProvider that logs events as .json files into the current working directory.

To set the global data provider, assign the DataProvider property on the static Audit.Core.Configuration object, or call the fluent API Use(). For example:

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

Or using the fluent API Use() method:

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

To set the data provider per-scope, use the AuditScopeOptions when creating an AuditScope. For example:

var scope = AuditScope.Create(new AuditScopeOptions 
{ 
  DataProvider = new MyCustomDataProvider(), ... }
);

Every data provider is accompanied by a fluent API accessible during object construction or via its respective Use___() method. For instance, in the case of the SqlDataProvider:

var sqlDataProvider = new SqlDataProvider(_ => _
    .ConnectionString("your connection string")
    .TableName("your table name")
    .IdColumnName("your id column name")
    .JsonColumnName("your json column name"));
Audit.Core.Configuration.Setup()
  .UseSqlServer(_ => _
    .ConnectionString("your connection string")
    .TableName("your table name")
    .IdColumnName("your id column name")
    .JsonColumnName("your json column name"));

Data provider wrappers

A special type of Data Providers that allows wrapping other Data Provider with different purposes.

Dynamic data provider

As an alternative to creating a data provider class, you can define the mechanism at run time by using the DynamicDataProvider or DynamicAsyncDataProvider classes. For example:

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

For async operations, you should use the DynamicAsyncDataProvider, for example:

Audit.Core.Configuration.Setup()
  .UseDynamicAsyncProvider(config => config
    .OnInsert(async ev => await File.WriteAllTextAsync(filePath, ev.ToJson())));

Lazy Factory data provider

You can set the global data provider using a deferred instantiation technique, with a lazy factory method that will be called upon its initial utilization. For instance, in situations where dependency resolution is needed but not immediately accessible during initialization.

Allows to lazily instantiate the data provider to use. The data provider factory method will be called only once; the first time it's needed.

For example:

Audit.Core.Configuration.Setup()
    .UseLazyFactory(() => app.ApplicationServices.GetService<CustomDataProvider>());

Deferred Factory data provider

You can defer creating the data provider for each Audit Event until it is ready to be saved by using a deferred factory method. The factory method will be called for each audit event being saved.

For example:

var sqlDataProvider = new SqlDataProvider(config => config...);
var fileDataProvider = new FileDataProvider(config => config...);

Audit.Core.Configuration.Setup()
    .UseDeferredFactory(auditEvent => auditEvent is AuditEventWebApi ? sqlDataProvider : fileDataProvider);

Conditional data provider

Enables the configuration of different data providers based on conditions related to the audit event.

For example:

Configuration.DataProvider = new ConditionalDataProvider(config => config
  .When(auditEvent => auditEvent.EventType.Equals("A"), new MyCustomDataProvider())
  .When(auditEvent => auditEvent.EventType.Equals("B"), new SqlDataProvider())
  .Otherwise(new FileDataProvider()));

Polly data provider

Allows to define Polly resilience strategies to any Data Provider.

This is useful when you want to add resilience to your data provider, for example, to retry failed operations, or to add a circuit breaker.

For example:

Audit.Core.Configuration.Setup()
  .UsePolly(p => p
    .DataProvider(new SqlDataProvider(...))
    .WithResilience(resilience => resilience
      .AddRetry(new()
      {
        ShouldHandle = new PredicateBuilder().Handle<SqlException>(),
        MaxRetryAttempts = 2
      })));

For more information, please refer to the Audit.NET.Polly documentation.

Data providers included

The Data Providers included are summarized in the following table:

Type Technology Package / Class Description Configuration API
SQL Amazon QLDB Audit.NET.AmazonQLDB / AmazonQldbDataProvider Store the audit events using Amazon QLDB. .UseAmazonQldb()
SQL Entity Framework Audit.EntityFramework / EntityFrameworkDataProvider Store EntityFramework audit events in the same EF context. (This data provider can only be used for Entity Framework audits) .UseEntityFramework()
SQL MySql Audit.NET.MySql / MySqlDataProvider Store the events as rows in a MySQL database table, in JSON format. .UseMySql()
SQL Postgre SQL Audit.NET.PostgreSql / PostgreSqlDataProvider Store the events as rows in a PostgreSQL database table, in JSON format. .UsePostgreSql()
SQL SQL Server Audit.NET.SqlServer / SqlDataProvider Store the events as rows in a MS SQL Table, in JSON format. .UseSqlServer()
NoSQL Azure Cosmos Audit.NET.AzureCosmos / AzureCosmosDataProvider Store the events in an Azure Cosmos DB container, in JSON format. .UseAzureCosmos()
NoSQL Azure Storage Audit.NET.AzureStorageBlobs / AzureStorageBlobDataProvider Store the events in an Azure Blob Storage container, in JSON format. .UseAzureStorageBlobs()
NoSQL Azure Tables Audit.NET.AzureStorageTables / AzureStorageTableDataProvider Store the events in an Azure Table Storage. .UseAzureTableStorage()
NoSQL Dynamo DB Audit.NET.DynamoDB / DynamoDataProvider Store audit events in Amazon DynamoDB™ tables. .UseDynamoDB()
NoSQL Elasticsearch Audit.NET.Elasticsearch / ElasticsearchDataProvider Store audit events in Elasticsearch indices. .UseElasticsearch()
NoSQL Kafka Audit.NET.Kafka / KafkaDataProvider Stream the audit events to Apache Kafka topics. .UseKafka() / .UseKafka<TKey>()
NoSQL Mongo DB Audit.NET.MongoDB / MongoDataProvider Store the events in a Mongo DB collection, in BSON format. .UseMongoDB()
NoSQL Raven DB Audit.NET.RavenDB / RavenDbDataProvider Store the events as documents in a Raven DB database table, in JSON format. .UseRavenDB()
NoSQL Redis Audit.NET.Redis / RedisDataProvider Store audit logs in Redis as Strings, Lists, SortedSets, Hashes, Streams or publish to a PubSub channel. .UseRedis()
Local Windows Event Log Audit.NET / Audit.NET.EventLog.Core 7 EventLogDataProvider Write the audit logs to the Windows EventLog. .UseEventLogProvider()
Local File System Audit.NET / FileDataProvider Store the audit logs as files. Dynamically configure the directory and path. .UseFileLogProvider()
InMemory In-Memory List Audit.NET / InMemoryDataProvider Store the audit logs in memory in a thread-safe list. Useful for testing purposes. .UseInMemoryProvider()
InMemory In-Memory Blocking Collection Audit.NET / BlockingCollectionDataProvider Store the audit events in a BlockingCollection that can be accessed by different threads to consume the events .UseInMemoryBlockingCollectionProvider()
InMemory In-Memory Channel Audit.NET.Channels / ChannelDataProvider Store the audit events in a Channel (from System.Threading.Channels) that can be accessed to consume the events .UseInMemoryChannelProvider()
Logging Log4net Audit.NET.log4net / Log4netDataProvider Store the audit events using Apache log4net™. .UseLog4net()
Logging NLog Audit.NET.NLog / NLogDataProvider Store the audit events using NLog. .UseNLog()
Logging Serilog Audit.NET.Serilog / SerilogDataProvider Store the audit events using Serilog™ .UseSerilog()
Network UDP Audit.NET.Udp / UdpDataProvider Send Audit Logs as UDP datagrams to a network. .UseUdp()
Wrapper Conditional Audit.NET / ConditionalDataProvider Allows the configuration of different data providers based on conditions related to the audit event. .UseConditional()
Wrapper Deferred Audit.NET / DeferredDataProvider Facilitates delayed data provider instantiation via a factory method that is invoked for each Audit Event. .UseDeferredFactory()
Wrapper Dynamic Audit.NET / DynamicDataProvider / DynamicAsyncDataProvider Dynamically change the behavior at run-time. Define Insert and a Replace actions with lambda expressions. .UseDynamicProvider() / .UseDynamicAsyncProvider()
Wrapper Lazy Audit.NET / LazyDataProvider Facilitates delayed data provider instantiation via a factory method that is invoked just once, upon the initial need. .UseLazyFactory()
Wrapper Polly Audit.NET.Polly / PollyDataProvider Allows to define Polly resilience strategies to any Data Provider .UsePolly()

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 (in 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(new AuditScopeOptions { CreationPolicy = EventCreationPolicy.Manual }))
{
    //...
    scope.Save();
}

Note

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

AuditScope statechart

The following is the internal state machine representation of the AuditScope object:

AuditScope statecart

Configuration

Data provider

To change the default data provider, set the static property DataProvider on Audit.Core.Configuration class. This should be done before 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();

Note

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 CreationPolicy on Audit.Core.Configuration class. This should be done before the AuditScope creation, i.e. during application startup.

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

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

Note

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 a certain 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);
});

Custom actions can also be asynchronous, for example:

Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, async scope =>
{
    var result = await svcProvider.GetService<InfoService>().GetInfoAsync();
    scope.SetCustomField("Info", result);
});

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.
  • OnEventSaved: After an Audit Scope's Event is saved.
  • OnScopeDisposed: When an Audit Scope is disposed.

Stack Trace

To include the stack trace details into the event environment, ensure that the IncludeStackTrace configuration is set to true. Default is false.

Audit.Core.Configuration.IncludeStackTrace = true;

or

Audit.Core.Configuration.Setup()
    .IncludeStackTrace();

Activity Trace

To include the current activity trace details from System.Diagnostics.Activity API into the event, ensure that the IncludeActivityTrace configuration is set to true. The default is false.

It will include the current Activity operation name, ID, and StartTime, along with associated Tags and Events.

Audit.Core.Configuration.IncludeActivityTrace = true;

or

Audit.Core.Configuration.Setup()
    .IncludeActivityTrace();

To create and start a new distributed activity trace, you can use the StartActivityTrace configuration:

Audit.Core.Configuration.Setup()
    .StartActivityTrace();

Global switch off

You can disable audit logging by setting the static property Configuration.AuditDisabled to true. The audit events are globally ignored while this flag is set. For example to disable the audits on certain environments:

if (environment.IsDevelopment())
{
    Audit.Core.Configuration.AuditDisabled = true;
}

Global serialization settings

Most of the data providers serialize audit events in JSON format. Audit.NET uses System.Text.Json by default for the serialization and deserialization of audit events.

If you want to change the behavior, you can change the settings via the static property Configuration.JsonSettings.

For example:

Audit.Core.Configuration.JsonSettings = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
    AllowTrailingCommas = true
};

Custom serialization mechanism

If you want to use a custom JSON serialization mechanism for the Audit Events, you can create a class implementing IJsonAdapter and assign it to the static property Configuration.JsonAdapter.

For example:

Audit.Core.Configuration.JsonAdapter = new MyCustomAdapter(); 

Or by using the fluent API:

Audit.Core.Configuration.Setup()
    .JsonAdapter<MyCustomAdapter>()
    ...

Note

Take into account that some of the AuditEvent properties rely on attribute decoration for serialization and deserialization. The recommendation is to use the default adapter and, when needed, use the Newtonsoft Json adapter provided (see next section).

Alternative serialization mechanism

This library offers the option to configure an alternative JSON serialization mechanism through the following adapter:

  • Audit.NET.JsonNewtonsoftAdapter

    Use this when you prefer employing Newtonsoft.Json as the serialization mechanism.

    Assign an instance of JsonNewtonsoftAdapter to the static configuration property Configuration.JsonAdapter. For example:

    var settings = new JsonSerializerSettings()
    {
        TypeNameHandling = TypeNameHandling.All
    };
    Audit.Core.Configuration.JsonAdapter = new JsonNewtonsoftAdapter(settings);

    Alternatively, you can use the fluent configuration API with the JsonNewtonsoftAdapter() method, like this:

    Audit.Core.Configuration.Setup()
        .JsonNewtonsoftAdapter(settings)
        ...

    NOTE: This JsonNewtonsoftAdapter takes into account JsonExtensionDataAttribute and JsonIgnoreAttribute decorators from both System.Text.Json and Newtonsoft.Json, so the Audit Events will be properly serialized.

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
    .IncludeStackTrace()
    .IncludeActivityTrace()
    .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)));
Multiple providers with conditional logic:
var sqlDataProvider = new SqlDataProvider(sql => sql.ConnectionString(CnnString).TableName("Logs"));
var fileDataProvider = new FileDataProvider(file => file.Directory(@"C:\logs"));

Audit.Core.Configuration.Setup()
    .UseConditional(c => c
        .When(ev => ev.EventType == "API", sqlDataProvider)
        .Otherwise(fileDataProvider));

Extensions

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

Package Description
icon Audit.DynamicProxy Generate detailed audit logs for any class without changing its code by using a proxy.
icon Audit.EntityFramework Generate detailed audit logs for saving operations on Entity Framework, by inheriting from a provided DbContext or IdentityDbContext. Includes support for EF 6 and EF 7 (EF Core).
icon Audit.FileSystem Generate audit logs by intercepting file system events via FileSystemWatcher.
icon Audit.HttpClient Generate detailed client-side audit logs for HttpClient REST calls, by configuring a provided message handler.
icon Audit.MVC Generate detailed audit logs by decorating MVC Actions and Controllers with an action filter attribute. Includes support for ASP.NET Core MVC.
icon Audit.SignalR Generate audit logs for SignalR and SignalR Core invocations by intercepting the hub processing
icon Audit.WCF Generate detailed server-side audit logs for Windows Communication Foundation (WCF) service calls, by configuring a provided behavior.
icon Audit.WCF.Client Generate detailed client-side audit logs for Windows Communication Foundation (WCF) service calls, by configuring a provided behavior.
icon Audit.WebApi Generate detailed audit logs by decorating Web API Methods and Controllers with an action filter attribute, or by using a middleware. Includes support for ASP.NET Core.
icon Audit.MongoClient Generate detailed audit logs by adding a Command Event Subscriber into the configuration of the MongoDB Driver.

Storage providers

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

Package Description
Audit.NET.AmazonQLDB Store the audit events in Amazon QLDB (Quantum Ledger Database).
Audit.NET.AzureCosmos Store the events in an Azure Cosmos DB container, in JSON format.
Audit.NET.AzureStorage Store the events in an Azure Blob Storage container or an Azure Table using the legacy client WindowsAzure.Storage.
Audit.NET.AzureStorageBlobs Store the events in an Azure Blob Storage container using the latest client Azure.Storage.Blobs.
Audit.NET.AzureStorageTables Store the events in an Azure Table Storage using the latest client Azure.Data.Tables.
Audit.NET.Channels Store the audit events in a Channel (from System.Threading.Channels) that can be accessed to consume the events.
Audit.NET.DynamoDB Store the audit events in Amazon DynamoDB tables.
Audit.NET.Elasticsearch Store the audit events in Elasticsearch indices.
Audit.NET.Kafka Stream the audit events to an Apache Kafka server.
Audit.NET.log4net Store the audit events using Apache log4net™.
Audit.NET.MongoDB Store the events in a Mongo DB Collection, in BSON format.
Audit.NET.MySql Store the events as rows in MySQL database, in JSON format.
Audit.NET.NLog Store the audit events using NLog™.
Audit.NET.Polly Define Polly resiliencie strategies to any data provider.
Audit.NET.PostgreSql Store the events as rows in a PostgreSQL database, in JSON format.
Audit.NET.RavenDB Store the events as documents in a Raven DB database, in JSON format.
Audit.NET.Redis Store Audit Logs in a Redis database as String, List, Hash, Sorted Set, Streams or publishing to a Redis PubSub channel.
Audit.NET.SqlServer Store the events as rows in a SQL Table, in JSON format.
Audit.NET.Serilog Store the audit events using Serilog™
Audit.NET.Udp Send audit events as UDP datagrams to a network.

Change Log

For detailed information on changes in the new release refer to the change log.

Contribute

If you like this project please contribute in any of the following ways:

audit.net's People

Contributors

adrianiftode avatar bgrainger avatar bromose avatar cristian-eriomenco avatar david-ernstsson-stratsys avatar deilan avatar dependabot[bot] avatar dmarciano avatar dolan-metcalf avatar elvin-gonzalez avatar farshaddavoudi avatar granadacoder avatar jefnic23 avatar jvereecken avatar koriseng avatar monkeywithacupcake avatar npnelson avatar ovidiaconescu avatar rcheung9 avatar simoncropp avatar snyk-bot avatar sparraguerra avatar sumboxcar182 avatar thearpee avatar thepirat000 avatar timloh-enjinstarter avatar tsangste avatar victorioberra avatar worming004 avatar zanyar3 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

audit.net's Issues

Invalid column name 'Id'.

Hi,

Me again.... Apologies for another intrusion.

To recap we are using .net core web api with .net 45 and EF 6.1.3

I am doing an update of an entity and am getting the error below every time I post a changed entity. All unit tests work however although those are written in a traditional c# test project. Any ideas why this would happen in the .Net Core api only?

{ "message": "Invalid column name 'Id'.", "detail": " at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext) at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters) at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass65`1.<ExecuteStoreQueryReliably>b__64() at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass65`1.<ExecuteStoreQueryReliably>b__63() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation) at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters) at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, ExecutionOptions executionOptions, Object[] parameters) at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source) at Audit.SqlServer.Providers.SqlDataProvider.InsertEvent(AuditEvent auditEvent) at Audit.Core.AuditScope.SaveEvent(Boolean forceInsert) at Audit.Core.AuditScope.Save() at Audit.EntityFramework.DbContextHelper.SaveScope(IAuditDbContext context, AuditScope scope, EntityFrameworkEvent event) at Audit.EntityFramework.DbContextHelper.SaveChanges(IAuditDbContext context, Func`1 baseSaveChanges) at Audit.EntityFramework.AuditDbContext.SaveChanges() at Link.Data.Services.SupplierRegistrationService.Upsert(SupplierRegistration entity) in C:\\Users\\Admin\\Source\\Repos\\LINK\\LINK\\Link.Data\\Services\\SupplierRegistrationService.cs:line 74 at Link.Api.Controllers.SupplierRegistrationsController.Put(SupplierRegistration entity) in C:\\Users\\Admin\\Source\\Repos\\LINK\\LINK\\Link.Api\\Controllers\\SupplierRegistrationsController.cs:line 40 at lambda_method(Closure , Object , Object[] ) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__27.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__25.MoveNext() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextExceptionFilterAsync>d__24.MoveNext()" }

Complex properties not tracked

Hello,

I'm not sure if missed anything in the docs.
I'm trying to track EF 6 entity changes.
For simple properties it works, but for complex properties (foreign key) the change is simply ignored.

Any suggestion?

Thanks,
Sandor

Scope nesting and db schema

Hi,
I'm find your library very promising for my project, but I have couple of q's:

  1. Can this library provide nested audit scopes? How I can do that?
    I need to track all things in http request boundary and I interested in writing single event to store,
    this event should hold data entries smth like:
    (Request) -> (Application scope) -> (Business scope) -> (EF scope)
    where Request and EF holding data which already implemented in your extensions(Mvc,Sql),
    Application and Business holding custom audit data

  2. How to make custom DB schema for storing event not in json?

Thanks in advance!
P.S: You making good stuff 👍

Exception occurred when input data has html code

Hi @thepirat000
I got an error when the request form data has html code, the Audit.Mvc.AuditAttribute class method private static IDictionary<string, string> ToDictionary(NameValueCollection col) occur an exception "System.Web.HttpRequestValidationException: 'A potentially dangerous Request.Form value was detected from the client (Html="
").'" , would you help to check this issue?
Below image for your reference.

image

CallingMethod and Assembly are one frame too high when using extensions

When using some extensions which implement attributes(currently at least Audit.Mvc and Audit.WebApi) and an action is audited, the record that is created contains something like this

        "AssemblyName": "Audit.Mvc, Version=4.10.0.0, Culture=neutral, PublicKeyToken=null",
        "CallingMethodName": "Audit.Mvc.AuditAttribute.OnActionExecuting()"

If you explicitly create the auditScope, it works as expected(meaning it references my assembly and calling method)

Guessing it's breaking because AuditScope.cs is hardcoded to 2, when, if coming from an attribute in this case, the stack would be one level deeper.

var callingMethod = new StackFrame(2).GetMethod();

This is super low priority however, I'm getting beautiful audits and they currently include the correct Controller and Action. Having the assembly and method would simply be sugar on top.

Thanks again for an awesome product.

Problem with inherited Entities

Hi,

I'm trying to integrate the Audit.NET library with my existing code. All of my entities inherit from an abstract base class. The code is not working and an error is thrown at the step "Get the entity type from the model that maps to the CLR type" in EntityKeyHelper.cs. The code is trying to find a CLR type for my base class, which cannot work properly because it doesn't exist in objectItemCollection. I'm not sure if it is correct to get the base entity type (GetBaseEntityType) for the given entity.

Regards
Sven

Constructor missing in AuditIdentityDBContext

I am trying to use this in one of my MVC 4.5 application and my DB context is defined as below:

public class MyDBContext : AuditIdentityDbContext<User>
    {
        public MyDBContext()
            : base("MyDBContext", throwIfV1Schema: false)
        {
        }
          //Additional DbSets here...
     }

While compiling the application it throws error in Constructor for - base("MyDBContext", throwIfV1Schema: false). I identified that the below constructor is missing in AuditIdentityDbContext and AuditDBContext class:

 /// <summary>
        ///  Constructor which takes the connection string to use
        /// </summary>
        /// <param name="nameOrConnectionString"></param>
        /// <param name="throwIfV1Schema">Will throw an exception if the schema matches that of Identity 1.0.0</param>
        public AuditIdentityDbContext(string nameOrConnectionString, bool throwIfV1Schema)
            : base(nameOrConnectionString)
        {
            if (throwIfV1Schema)
            {
                _helper.SetConfig(this);
            }
        }  

I added this constructor in Audit.EntityFramework Source in both the classes and added a reference of that source in Project and it started working fine.

Would you suggest to add this in this package?

Please let me know in case you need any additional details.

Thanks,
Dhaval Limbad

Audit.WebApi Never Log Responce Content

@thepirat000 Because System.Web.Http.Filters.ActionFilterAttribute.OnActionExecuted method HttpActionExecutedContext.Response.Content will be a unsekable stream when using owin, auditAction.ResponseBody will always be empty.

To fix it ,I added a Middleware to use bufferred stream to replace networkInputStream, then I can get the HttpActionExecutedContext.Response.Content and read it as string.
I posted here angularsen/AspNetHttpLogger#3.

I think you`d better add a Notice on README or somewhere, or fix it in you way, that will helpful.

Environment object on IIS is just the user running the IIS server

I'm running Audit.NET on an API deployed in IIS. The environment data for each call that gets audited is always the same and pretty much of no value, as follows:

"Environment": {
    "UserName": "DotNet Core",
    "MachineName": "HOFFELL",
    "DomainName": "IIS APPPOOL",
    "CallingMethodName": "SomeController.Get()",
    "AssemblyName": "Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
    "Culture": "is-IS"
},

Is it possible to pass in more useful information, and how to achieve it, related to the user that is calling the api?

Multitenancy?

Hi!
Is there a way to set the connection string to the azure storage per request?

Our customers have there own azure storage accounts and we want to be able to store there own audit logs in there own azure storage. (They are using our SAAS webapi)

Regards
Per

Unable to query CustomFields in MongoDBDataProvider

Amazing library. :)

I am trying to query single audit event by ObjectId (593feb7c2d63d072b061d966) of AuditEvent(_id) to get the difference in a Old and New document.

here is my query

mongoDataProvider.QueryEvents()
             .FirstOrDefault(ev => ev.CustomFields.Any(kvp => kvp.Key = "_id" (string)kvp.Value == auditId));

Its throwing Exception "The expression tree is not supported".

Here is sample document

{ 
    "_id" : ObjectId("593feb7c2d63d072b061d966"), 
    "Environment" : {
        "UserName" : "Patel, Anand", 
        "MachineName" : "APATEL-W10LT", 
        "DomainName" : null, 
        "CallingMethodName" : null, 
        "AssemblyName" : null, 
        "Exception" : null, 
        "Culture" : "en-US"
    }, 
    "EventType" : "593aaedc8f0dd20019fbdd40", 
    "Target" : {
        "Type" : "Template", 
        "Old" : {
            "_t" : "MongoDB.Bson.BsonDocument, MongoDB.Bson", 
            "_v" : {
                "_t" : "Template", 
                "_id" : ObjectId("593aaedc8f0dd20019fbdd40"), 
                "name" : "Import Test - 12", 
                "type" : "Web", 
                "activeIndicator" : true, 
                "parentId" : "5925d50336ed6f0019b6a660", 
                "parentIndicator" : false, 
                "createdAtUtc" : ISODate("2017-06-09T14:21:16.736+0000"), 
                "updatedAtUtc" : ISODate("0001-01-01T00:00:00.000+0000")
            }
        }, 
        "New" : {
            "_t" : "MongoDB.Bson.BsonDocument, MongoDB.Bson", 
            "_v" : {
                "_t" : "Template", 
                "_id" : ObjectId("593aaedc8f0dd20019fbdd40"), 
                "name" : "Import Test - 1222", 
                "type" : "Web", 
                "activeIndicator" : true, 
                "parentId" : "5925d50336ed6f0019b6a660", 
                "parentIndicator" : false, 
                "createdAtUtc" : ISODate("2017-06-09T14:21:16.736+0000"), 
                "updatedAtUtc" : ISODate("0001-01-01T00:00:00.000+0000")
            }
        }
    }, 
    "Comments" : [
        "Template imported."
    ], 
    "StartDate" : ISODate("2017-06-13T13:41:16.193+0000"), 
    "EndDate" : ISODate("2017-06-13T13:41:16.268+0000"), 
    "Duration" : NumberInt(75)
}

SQL Server Support for Schemas

I have a database with several schemas in it. I am currently using the API audit package and I want the event table to be stored in a certain schema. How can I configure the Audit.NET to work with a specific schema? Or is this something that will need to be coded into the application.

Audit.WebApi Never Log Responce Content

@thepirat000 Because System.Web.Http.Filters.ActionFilterAttribute.OnActionExecuted method HttpActionExecutedContext.Response.Content will be a unsekable stream when using owin, auditAction.ResponseBody will always be empty.

To fix it ,I added a Middleware to use bufferred stream to replace networkInputStream, then I can get the HttpActionExecutedContext.Response.Content and read it as string.
I posted here angularsen/AspNetHttpLogger#3.

I think you`d better add a Notice on README or somewhere, or fix it in you way, that will helpful.

Disable Audit for a Table it remains tracking for delete.

Hello,

I used this code for disable audit for some tables:

            Audit.EntityFramework.Configuration.Setup()
                            .ForContext<MyContext>(config => config
                            .IncludeEntityObjects(false)
                            .AuditEventType("{context}:{database}"))
                            .UseOptOut()
                            .IgnoreAny(x => x.Name == "LogCmdClient" || x.Name == "BlackListBatch" || x.Name ==          "BlackListSQL" || x.Name == "ReleaseFiles");

But I noticed that when I delete something generates an occurrence in my audit log.

Track only crUD changes.

Is there a way I only track Update and Delete actions of my models?
And its also capture DBUpdate Exceptions, where changes were not successful. How can I exclude these as well?
"Valid":true,"ValidationResults":[]}],"Result":0,"Success":false,"ErrorMessage":"(DbUpdateException) An error occurred while updating the entries. See the inner exception for details.

Missing strong name

Hi,

Would it be possible to strongly sign your release version that you package in your Nuget Package. This way the nuget can also be used in projects that require all assemblies to be signed. Make sure you make a wise decision on your key because if you change it in future versions other issues come up for backwards compatibility.

Kind regards,
Bjorn

Is there any way to improve performance?

I have tried Audit.Mvc with FileDataProvider and made a performance test as shown below:

Audit.Core.Configuration.Setup()
                .UseFileLogProvider(config => config
                    .DirectoryBuilder(_ => $@"C:\Logs\{DateTime.Now:yyyy-MM-dd}")
                    .FilenamePrefix("Event_"));
    [Audit]
    public IActionResult Index()

result:

Speed=34177 pages/min
    //[Audit]
    public IActionResult Index()

result:

Speed=137437 pages/min

I think that wrote audit data immediately, or into independent files will cause performance issue. Is there any way to write all audit data into one file or make a batch writing?

Audit.Net WebApi AND EF6

Hi I'm currently using Audit.NET for WebApi. I've im using the attribute based approach for auditing specific api calls. This works great.
However, now I want to include whatever changes that API calls have made to the underlying EF6 scope.
My question is; can I include both webapi and ef6 audit information in the same audit record?
Also, how do I ensure that all the other EF6 are not audited?
Any ideas or thoughts?

Configuration of MVC project with EF6

Hi,
I'm testing your library for the auditing in my application that uses EF6.
I configured SQL provider in Application_Start.

With first of the AuditScope use I got this exception:
The default DbConfiguration instance was used by the Entity Framework before the 'DbConfig' type was discovered. An instance of 'DbConfig' must be set at application start before using any Entity Framework features or must be registered in the application's config file. See http://go.microsoft.com/fwlink/?LinkId=260883 for more information.

Can you explain how to use it in MVC app with EF6 in use and the same database as target ?

Audit.MVC CreationPolicy weirdness

Setting CreationPolicy to InsertOnEnd is behaving the same as setting it to InsertOnStartInsertOnEnd. Specifically it appears to be firing the Insert event for OnActionExecuted and OnResultExecuted. It is my understanding that InsertOnEnd should cause it to only fire the insert event when the scope is disposed, which happens in OnResultExecuted.

This is what I'm attempting to use:

Configuration.Setup()
	.UseDynamicProvider(config => config.OnInsert(auditEvent =>
		//Do something
	))
	.WithCreationPolicy(EventCreationPolicy.InsertOnEnd);

The result of this is that I'm getting nearly duplicate log entries.

Thoughts?

lose the Authentication after mvc Update. Cause is audit.net

Hi Daniel,
the audit.net runs on my project since over two months, but I updated all packages yesterday, now I lose Authentication. I deactivated audit configuration then works well fine. I using following code in my asp.net core startup. I wanna ask you have you any idee ? Thank you very much

app.Use(async (context, next) =>
{
Audit.Core.Configuration.AddCustomAction(ActionType.OnEventSaving, scope =>
{
if (scope.Event.Environment.Exception != null)
{
scope.SetCustomField("Fehler", true);
}
if (context.User.Identity.Name != null)
{
try
{
scope.Event.Environment.UserName = context.User.Identity.Name.ToString();
scope.Event.Environment.MachineName = context.Connection.RemoteIpAddress.ToString();
}
catch (Exception)
{
scope.Event.Environment.UserName = "Fehler";
scope.Event.Environment.MachineName = "Fehler";
}
}
});
if (context.Request.IsHttps)
{
await next();
}
else
{
var withHttps = "https://" + context.Request.Host + context.Request.Path;
context.Response.Redirect(withHttps);
}
});

Not getting changes tracked?

Hey, I've been trying today to get a combination of Audit.NET SqlServer and EF tracking changes to my database. I'm getting events to fire, but I'm not able to get any changes to be tracked? Instead I seem to get an empty list in my DB.

Is there anything in particular I need to know?

I'm configured with OptOut, and AnyContext - and my DbContext is an extension of AuditContext. I get the new state of the object perfectly fine, but no information on changes that have been made.

Audit.WebApi can`t work in selfhost webapi

Audit.WebApi can`t work in selfhost webapi , because use System.Web.dll as a reference.
It will fire a ArguementNullException, because HttpContext.Current is null.
I think using owinContext as a "WebContext" environment will make it more extensional.

Audit.WebAPI Ignores EventCreationPolicy

I've come across an issue whereby the WebAPI extension appears to be ignoring the EventCreation Policy.

I've set the policy in two ways, both in the fluent style as follows:

			Audit.Core.Configuration.CreationPolicy = EventCreationPolicy.InsertOnStartReplaceOnEnd;
			var connectionString = ConfigurationManager.ConnectionStrings[contextName].ConnectionString;
			Audit.Core.Configuration
				.Setup()
				.UseSqlServer(config => config.ConnectionString(connectionString)
						.Schema("dbo")
						.TableName("events")
						.IdColumnName("id")
						.JsonColumnName("data")
						.LastUpdatedColumnName("lastUpdatedDate")
						)
				.WithCreationPolicy(EventCreationPolicy.InsertOnStartReplaceOnEnd);

however all my events are only being written to the log at the end of the request, and none of the events have a populated 'lastUpdatedField'

Multiple EntityTypeMapping elements in EDMX

Hi, and let me start by saying thanks for the awesome project.

I recently integrated Audit.EntityFramework into an existing project that uses an EDMX file. It worked perfectly, except for one entity that is mapped to modification functions. After some digging, I discovered that the designer had created two EntityTypeMapping elements for the entity, one containing the MappingFragment, and the other containing the ModificationFunctionMapping. This appears to be perfectly valid inside the various EF components, but results in an InvalidOperationException ("Sequence contains more than one element.") on line 161 of EntityKeyHelper.cs.

I've managed to get around the error by merging the two EntityTypeMapping elements in the EDMX, but since the designer has a mind of it's own, I wouldn't be surprised if it puts things back to the way they were the next time the file is updated from the database.

I noticed that, at runtime, EF had merged the function mapping metadata into the EntityTypeMapping instance that contained the fragment, however the opposite was not true, so it might be possible to prevent the error by filtering out EntityTypeMapping instances that don't contain any fragments. i.e. Line 161 could be updated to:

            .EntityTypeMappings.Single(m => m.Fragments.Count != 0)

Regards
Rory

Multiple Data Providers

Is it possible to have multiple data providers, one to capture all the Web API activities and another to capture the Entity Framework (data layer) activities, on separate tables?

Audit Default Directory File Output

I'm not sure how to change the default Directory. It always save to IIS express when I run in local. Do you have example code for this?

Please assists. Thanks.

public class CustomAuditDataProvider : Audit.Core.Providers.FileDataProvider
    {
        public override object InsertEvent(AuditEvent auditEvent)
        {
            //FileDataProvider fileDataProvider = new FileDataProvider();

            DirectoryPath = "C://Users//XXXX";
            // 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;
        }

    }

SQL Server Provider - Trigger on Event table

I tried to create a trigger on the Event table. Whenever I wanted to add an event log in the database (with the Audit.NET.SqlServer provider) I have an issue due to the way the log is inserted in the database.
I searched in the code of the provider (SqlDataProvider.cs) and found out that it use this query to insert a line in the table :
INSERT INTO {0} ([{1}]) OUTPUT CONVERT(NVARCHAR(MAX), INSERTED.[{2}]) AS [Id] VALUES (@json)
But the trigger will always fail if the statement contains an OUTPUT.

Here is the exception :

An exception of type 'System.Data.SqlClient.SqlException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: The target table 'dbo.Event' of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.

I did work around this issue by replacing the INSERT statement by INSERT INTO {0} ([{1}]) VALUES (@json) in the SqlDataProvider.cs (I downloaded the source of the project) file and it works just fine for me.

ContainerNameBuilder?

Hi and thanks for the awesome Audit package you created :)

We are creating a lot of audit logs in azure and we need to remove them when they reach a certain age.
Deleting the files one by one takes to long time. If there were a ContainerNameBuilder, I could create a new container per month, and delete this container after a certain time and all the files will be deleted with it :)

Another question: Whats the performance cost for auditing to azure blobs? Are you batching the updates? Runing the update async or on another thread?

Regards
//Per

Can this library can be used in MVC application using .Net Framework 4.0/3.5 ?

Firstly, Thanks a lot for creating this Awesome Library. I love using it.
I am new to package and have came across below issue while using this in one my application.
I added reference of Audit.net 6.2.0 in my MVC application built on .Net Framework 4.0.
In Global.asax Start_Application event when i use below configuration, it gives me System.Object error:

Audit.Core.Configuration.DataProvider = new Audit.SqlServer.Providers.SqlDataProvider()
{
ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["AuditDBConnection"].ConnectionString,
Schema = "odb",
TableName = "LOG_JSON",
IdColumnName = "LOG_JSON_ID",
JsonColumnName = "JSON_STRING",
LastUpdatedDateColumnName = "CREATED_DATE" ,

        };

Error i get while using "Audit.Core.Configuration" in first line :

The type 'System.Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Does not install with Newtownsoft V9

Hi,
When I tried to install Audit.MVC

Installing Audit.NET 6.0.0.
Adding package 'Audit.NET.6.0.0' to folder 'c:\testaud\packages'
Added package 'Audit.NET.6.0.0' to folder 'c:\testaud\packages'
Install failed. Rolling back...
Package 'Audit.NET.6.0.0 : Newtonsoft.Json [9.0.1, )' does not exist in project 'TestAud'
Removing package 'Audit.NET.6.0.0 : Newtonsoft.Json [9.0.1, )' from folder 'c:\testaud\packages'
Removed package 'Audit.NET.6.0.0 : Newtonsoft.Json [9.0.1, )' from folder 'c:\testaud\packages'
Install-Package : Failed to add reference to 'Audit.NET'.
At line:1 char:1
+ Install-Package Audit.MVC
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand

Mark Audit.NET CLS Compliant

Would it be possible to mark the Audit.NET assembly CLS compliant by adding the ClsCompliantAttribute to the assembly.info file?

[assembly: CLSCompliant(true)]

Thanks for considering.

Audit.EntityFramework: Old and new value are always the same

Hi!

I´m using Audit.EntityFramework and on log output the old and new values for changed entity are always the same!

My config on statup.cs:

Audit.Core.Configuration.Setup().UseSqlServer(c => {
                c.ConnectionString(Configuration.GetConnectionString("audit.db"));
                c.Schema("dbo");
                c.TableName("Event");
                c.IdColumnName("EventId");
                c.JsonColumnName("Data");
                c.LastUpdatedColumnName("LastUpdatedDate");
                
            });

            Audit.EntityFramework.Configuration.Setup()
                .ForAnyContext(x => x.IncludeEntityObjects(true)
                                     .AuditEventType("{context}:{database}"))
                .UseOptOut();

Thankyou for the library and support!

Responce Body + Client IP ADDRESS

Hello,

We have 4 apps which are already in production but for auditing we have to write a lot of code so we were looking for some robust mechanism with same functionality but with less code.

I am loving it to use AUDIT.NET believe me it only takes one day to attach to all apps.
Thanks.

Well i am looking for two things

  1. Is there any way to store client IP in Insert-event Method.
  2. I want to store both client request JSON and response body JSON .

Thanks

Default Configuration for ASP MVC

Hi,

I was looking at your help for the APS MVC 4 / 5, the picture really helped.

Is there default configuration on startup to just audit all actions/or atleast by controller, rather than putting on each action(by roles)? to profile things like number of times a password was failed/ reset etc

thanks

object reference not set error in DbContextHelper.SetConfig

Hi,

This great library has saved us ages in getting good auditing set up, so many thanks indeed!!

We have one small problem with it. We are using a dotnet 452 mvc app and in the Startup.startup method we are configuring a SQLProvider as follows

        public Startup(IHostingEnvironment env)
        {
            //Load application config as the first step in the startup
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
            builder.AddEnvironmentVariables();
            Configuration = builder.Build();

            //Set up a storage provider for the audit infrastructure that we are using
            //See https://github.com/thepirat000/Audit.NET
            var sqlProvider = new Audit.SqlServer.Providers.SqlDataProvider();
            sqlProvider.ConnectionString = Configuration.GetConnectionString("Link.Core.Db");
            sqlProvider.Schema = "audit";
            sqlProvider.LastUpdatedDateColumnName = "LastUpdateTime";
            Audit.Core.Configuration.Setup().UseCustomProvider(sqlProvider);

        }

We then instantiate our AuditDbContext object in the ConfigureServices method as per:

    public void ConfigureServices(IServiceCollection services) {
        //Other service setup omitted for brevity
        services.AddScoped(_ => new LinkContext(Configuration.GetConnectionString("Link.Core.Db")));
   }

The first time we hit the API service we are getting the following error raised:

System.NullReferenceException was unhandled by user code
Message=Object reference not set to an instance of an object
StackTrace:
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at Audit.EntityFramework.DbContextHelper.SetConfig(IAuditDbContext context)
    at Audit.EntityFramework.AuditDbContext..ctor(String nameOrConnectionString)
    at Link.Data.LinkContext..ctor(String connString)
    at Link.Api.Startup.<ConfigureServices>b__4_2(ISeachProvider _)
    ........ Remaining stack trace omitted for brevity

It appears to work subsequent to this so wondering if we are starting up something in the wrong order?

Thanks in advance

Iain

In-Memory Database

Does Audit.EntityFramework not work with in-memory database? I need it for testing. I am using EF Core with xUnit.

I have a context factory like so:

public class DatabaseContextFactory
{
    public DatabaseContext Create()
    {
        var serviceProvider = new ServiceCollection()
            .AddEntityFrameworkInMemoryDatabase()
            .BuildServiceProvider();

        var builder = new DbContextOptionsBuilder<DatabaseContext>();
        builder.UseInMemoryDatabase()
            .UseInternalServiceProvider(serviceProvider);

        return new DatabaseContext(builder.Options);
    }
}

Where my DatabaseContext inherits from AuditDbContext:

public sealed class DatabaseContext : AuditDbContext
{
    public DatabaseContext(DbContextOptions options)
        : base(options)
    {
    }
}

I get this error when running tests:

System.IndexOutOfRangeException : Index was outside the bounds of the array.
Stack Trace:
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at Audit.EntityFramework.DbContextHelper.EnsureEntitiesIncludeIgnoreAttrCache(Type type)
    at Audit.EntityFramework.DbContextHelper.IncludeEntity(IAuditDbContext context, EntityEntry entry,  AuditOptionMode mode)
    at Audit.EntityFramework.DbContextHelper.<>c__DisplayClass19_0.<GetModifiedEntries>b__0(EntityEntry x)
    at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
    at Audit.EntityFramework.DbContextHelper.GetModifiedEntries(IAuditDbContext context)
    at Audit.EntityFramework.DbContextHelper.CreateAuditEvent(IAuditDbContext context)
    at Audit.EntityFramework.DbContextHelper.SaveChanges(IAuditDbContext context, Func`1 baseSaveChanges)
    at Audit.EntityFramework.AuditDbContext.SaveChanges()

Feature: Direct support for EntityFramework

Hi Frederico,

While playing around with Audit.NET I would love to see direct integration of EntityFramework.
I mean a possibility to save EventEntry and other data (POCO objects) directly to existing DbContexts via DbSets.
Currently I'm trying to implement my own CustomDataProvider to achieve this goal. I will try to fetch all EntityFrameworkEvents and use their data for saving.

The reason for this: I need a possibility to show the end user changes made by other users on an entity. That way I need the data in plain format to show in data grids - not json.

Regards
Sven

Collection was modified; enumeration operation may not execute.

Hi,
I encountered a issue when using AuditScope in my MVC application like below.

AuditScope.CreateAndSave("LoginSuccess", new { username = user.username });

AuditScope.CreateAndSave("LoginFailed", new { username = user.username });

I have several controller action calls the log , however it doesn't always error out. Sometime it throws exception, and some time it works well.
the exception message is as the title:

Collection was modified; enumeration operation may not execute.

the stack trace logged as follows:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.List1.Enumerator.MoveNextRare() at Audit.Core.Configuration.InvokeScopeCustomActions(ActionType type, AuditScope auditScope) at Audit.Core.AuditScope.SaveEvent(Boolean forceInsert) at Audit.Core.AuditScope..ctor(String eventType, Func1 target, Object extraFields, AuditDataProvider dataProvider, Nullable`1 creationPolicy, Boolean isCreateAndSave)
at Audit.Core.AuditScope.CreateAndSave(String eventType, Object extraFields, AuditDataProvider dataProvider)

So my when i looked up the source code, in the class Audit.Core.Configuration , you have

internal static void InvokeScopeCustomActions(ActionType type, AuditScope auditScope)
{
            foreach (var action in AuditScopeActions[type])
            {
                action.Invoke(auditScope);
            }
}

the above function loop through the AuditScopeActions Dictionary defined as

internal static Dictionary<ActionType, List<Action<AuditScope>>> AuditScopeActions { get; private set; }

Could that be because the Dictionary isn't thread safe, so when multiple thread from my MVC web application reads and writes it concurrently, it throws the error?

let me know if i am mistaken. thank you.

Hiding certain for fields or action parameters

I have some sensitive information that comes across in form fields and action parameters that I would like to either remove from the log or encrypt.

I see no way of doing this without a custom policy creation.
I was hoping (for example the MVC extension) I could hook into it and remove different fields I did not want to log at all?

Is this possible?

AuditApiAttribute NullReferenceException

Hi, the following is the response , and found that the potential Null Reference Exception.

{
"Message": "An error has occurred."
"ExceptionMessage": "Object reference not set to an instance of an object."
"ExceptionType": "System.NullReferenceException"
"StackTrace": " at Audit.WebApi.AuditApiAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext) at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at System.Web.Http.Filters.ActionFilterAttribute.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"
}

AuditApiAttribute class
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
actionExecutedContext.Response <--- may null if the exception throw.

Following is my modified code AuditApiAttribute.

 public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var httpContext = GetHttpContext(actionExecutedContext.Request);
        var auditAction = httpContext.Items[AuditApiActionKey] as AuditApiAction;
        var auditScope = httpContext.Items[AuditApiScopeKey] as AuditScope;
        if (auditAction != null && auditScope != null)
        {
            auditAction.Exception = actionExecutedContext.Exception.GetExceptionInfo();
            auditAction.ModelStateErrors = IncludeModelState ? GetModelStateErrors(actionExecutedContext.ActionContext.ModelState) : null;
            auditAction.ModelStateValid = IncludeModelState ? (bool?)actionExecutedContext.ActionContext.ModelState.IsValid : null;
            auditAction.ResponseBodyType = actionExecutedContext.Response == null ? "" : actionExecutedContext.Response.Content.GetType().Name;


            if (actionExecutedContext.Response != null)
            {

                auditAction.ResponseStatus = actionExecutedContext.Response.ReasonPhrase;
                auditAction.ResponseStatusCode = (int)actionExecutedContext.Response.StatusCode;
                if (IncludeResponseBody)
                {
                    var objContent = actionExecutedContext.Response.Content as ObjectContent;
                    auditAction.ResponseBody = objContent != null
                        ? new { Type = objContent.ObjectType.Name, Value = objContent.Value }
                        : (object)actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
                }
            }
            else
            {
                auditAction.ResponseStatusCode =  500 ;
                auditAction.ResponseStatus = "Internal Server Error";

            }
            // Replace the Action field and save
            auditScope.SetCustomField("Action", auditAction);
            auditScope.Save();
        }
    }

Audit.WebApi: audit \Token call

We have plugged in the Audit.WebApi via a global filter. but the authentication call to \Token is not being recorded.

I assume this is because it is not a ApiController call.

any idea how i could get this to work

Missing method exception in GetEntityName in AuditDbContext.Core.cs

entityType.SqlServer() method is not found when trying to get entity name

System.MissingMethodException: Method not found: 'Microsoft.EntityFrameworkCore.Metadata.IRelationalEntityTypeAnnotations Microsoft.EntityFrameworkCore.SqlServerMetadataExtensions.SqlServer(Microsoft.EntityFrameworkCore.Metadata.IEntityType)'. at Audit.EntityFramework.AuditDbContext.GetEntityName(IEntityType entityType) at Audit.EntityFramework.AuditDbContext.CreateAuditEvent(Boolean includeEntities, AuditOptionMode mode) at Audit.EntityFramework.AuditDbContext.SaveChanges()

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.