Skip to content

Commit

Permalink
Adding Weak Reference to AuditScope in the AuditEvent. Introducing st…
Browse files Browse the repository at this point in the history
…ruct Audit.Core.Setting<T>
  • Loading branch information
thepirat000 committed Feb 16, 2024
1 parent f99888a commit 7fe33c8
Show file tree
Hide file tree
Showing 66 changed files with 771 additions and 850 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ All notable changes to Audit.NET and its extensions will be documented in this f

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## [24.0.2] - 2024-02-:
## [25.0.0] - 2024-02-16:
- Audit.NET: Adding a Weak Reference to the AuditScope in the AuditEvent class, to allow accessing the current AuditScope from the AuditEvent.
- Audit.NET: Introducing a new struct `Audit.Core.Setting<T>` to standarize Data Provider settings, allowing the value to be set directly or as a function of the `AuditEvent`.
Refactoring the majority of the Data Providers to use the new `Setting<T>`.

## [24.0.1] - 2024-02-12:
- Audit.NET.Polly: Adding GetAuditEvent extension method to ResilienceContext.
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>24.0.2</Version>
<Version>25.0.0</Version>
<PackageReleaseNotes></PackageReleaseNotes>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
</PropertyGroup>
Expand Down
153 changes: 88 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,18 @@ Generate [audit logs](https://en.wikipedia.org/wiki/Audit_trail) with evidence f

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.

[**Interaction extensions**](#extensions) to audit different systems are provided, such as [Entity Framework](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.EntityFramework/README.md),
[MVC](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.Mvc/README.md),
[WebAPI](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.WebApi/README.md),
[WCF](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.WCF/README.md),
[File System](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.FileSystem/README.md),
[SignalR](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.SignalR/README.md),
[MongoClient](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.MongoClient/README.md)
and [HttpClient](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.HttpClient/README.md).

[**Output extensions**](#storage-providers) are provided to log to [JSON Files](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET/Providers/FileDataProvider.cs),
[Event Log](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET/Providers/EventLogDataProvider.cs), [SQL](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.SqlServer/README.md),
[Event Log](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET/Providers/EventLogDataProvider.cs),
[SQL](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.SqlServer/README.md),
[MySQL](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.MySql/README.md),
[PostgreSQL](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.PostgreSql/README.md),
[RavenDB](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.RavenDB/README.md),
Expand All @@ -26,14 +36,13 @@ With Audit.NET you can generate tracking information about operations being exec
[DynamoDB](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.DynamoDB/README.md),
[UDP datagrams](https://github.com/thepirat000/Audit.NET/tree/master/src/Audit.NET.Udp/README.md) and more.

[**Interaction extensions**](#extensions) to audit different systems are provided, such as [Entity Framework](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.EntityFramework/README.md),
[MVC](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.Mvc/README.md),
[WebAPI](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.WebApi/README.md),
[WCF](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.WCF/README.md),
[File System](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.FileSystem/README.md),
[SignalR](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.SignalR/README.md),
[MongoClient](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.MongoClient/README.md)
and [HttpClient](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.HttpClient/README.md).
[**Output wrappers**](#data-providers-wrappers) are included
to facilitate the encapsulation of other Data Provider for diverse purposes, like resilience or lazy instantiation, such as
[Polly](#polly-data-provider),
[Lazy](#lazy-data-provider),
[Deferred](#deferred-data-provider) and
[Conditional](#conditional-data-provider).


## [NuGet](https://www.nuget.org/packages/Audit.NET/)

Expand Down Expand Up @@ -520,7 +529,7 @@ The data provider can be set globally for the entire application or per audit sc
>
> 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:
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:

```c#
Audit.Core.Configuration.DataProvider = new MyCustomDataProvider();
Expand All @@ -542,45 +551,34 @@ var scope = AuditScope.Create(new AuditScopeOptions
);
```

#### 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 necessitated but not immediately accessible during initialization:
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]((https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.SqlServer/README.md)):

```c#
Audit.Core.Configuration.Setup()
.UseLazyFactory(() => app.ApplicationServices.GetService<CustomDataProvider>());
var sqlDataProvider = new SqlDataProvider(_ => _
.ConnectionString("your connection string")
.TableName("your table name")
.IdColumnName("your id column name")
.JsonColumnName("your json column name"));
```

#### 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**.
This factory method will be called for each Audit Event. For example:

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

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

.UseSqlServer(_ => _
.ConnectionString("your connection string")
.TableName("your table name")
.IdColumnName("your id column name")
.JsonColumnName("your json column name"));
```

See [Wrapper data providers](#wrapper-data-providers) for more information.
### Data provider wrappers

### Dynamic data providers
A special type of Data Providers that allows wrapping other Data Provider with different purposes.

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:
#### Dynamic data provider

```c#
var dataProvider = new DynamicDataProvider();
// Attach an action for insert
dataProvider.AttachOnInsert(ev => Console.Write(ev.ToJson()));
Audit.Core.Configuration.DataProvider = dataProvider;
```
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:

Or by using the fluent API:

```c#
Audit.Core.Configuration.Setup()
.UseDynamicProvider(config => config
Expand All @@ -590,51 +588,76 @@ Audit.Core.Configuration.Setup()
For async operations, you should use the `DynamicAsyncDataProvider`, for example:

```c#
var dataProvider = new DynamicAsyncDataProvider();
dataProvider.AttachOnInsert(async ev => await File.WriteAllTextAsync(filePath, ev.ToJson()));
Audit.Core.Configuration.DataProvider = dataProvider;
Audit.Core.Configuration.Setup()
.UseDynamicAsyncProvider(config => config
.OnInsert(async ev => await File.WriteAllTextAsync(filePath, ev.ToJson())));
```

Or by using the fluent API:

#### 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:

```c#
Audit.Core.Configuration.Setup()
.UseDynamicAsyncProvider(config => config
.OnInsert(async ev => await File.WriteAllTextAsync(filePath, ev.ToJson())));
.UseLazyFactory(() => app.ApplicationServices.GetService<CustomDataProvider>());
```

### Wrapper data providers
#### Deferred Factory data provider

A special type of Data Providers that allows wrapping other Data Providers with different purposes:
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.

- LazyDataProvider
For example:

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.
```c#
var sqlDataProvider = new SqlDataProvider(config => config...);
var fileDataProvider = new FileDataProvider(config => config...);

```c#
Configuration.DataProvider = new LazyDataProvider(() => app.ApplicationServices.GetService<MyCustomDataProvider>());
```
Audit.Core.Configuration.Setup()
.UseDeferredFactory(auditEvent => auditEvent is AuditEventWebApi ? sqlDataProvider : fileDataProvider);
```

#### Conditional data provider

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

For example:

Allows to defer the data provider instantiation until the audit event is about to be saved. The data provider factory method will be called for each audit event being saved.
```c#
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()));
```

```c#
Configuration.DataProvider = new DeferredDataProvider(auditEvent => auditEvent is AuditEventWebApi ? new FileDataProvider() : EventLogDataProvider());
```
#### Polly data provider

- ConditionalDataProvider
Allows to define [Polly](https://www.pollydocs.org/index.html) resilience strategies to any [Data Provider](https://github.com/thepirat000/Audit.NET?tab=readme-ov-file#data-providers).

Enables the configuration of different data providers based on conditions related to the audit event.
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.

```c#
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()));
```
For example:

#### Data providers included
```c#
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](https://github.com/thepirat000/Audit.NET/blob/master/src/Audit.NET.Polly/README.md).

### Data providers included

The Data Providers included are summarized in the following table:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static ICreationPolicyConfigurator UseAmazonQldb(this IConfigurator confi
Configuration.DataProvider = new AmazonQldbDataProvider
{
QldbDriver = driverFactory,
TableNameBuilder = tableNameBuilder
TableName = tableNameBuilder
};
return new CreationPolicyConfigurator();
}
Expand All @@ -38,8 +38,8 @@ public static ICreationPolicyConfigurator UseAmazonQldb(this IConfigurator confi
var provider = new AmazonQldbDataProvider
{
QldbDriver = amazonQldbProviderConfigurator._driverFactory,
TableNameBuilder = amazonQldbProviderConfigurator._tableConfigurator?._tableNameBuilder,
CustomAttributes = amazonQldbProviderConfigurator._tableConfigurator?._attrConfigurator?._attributes
TableName = amazonQldbProviderConfigurator._tableConfigurator._tableName,
CustomAttributes = amazonQldbProviderConfigurator._tableConfigurator._attrConfigurator?._attributes
};
if (amazonQldbProviderConfigurator._jsonSettings != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ namespace Audit.AmazonQLDB.ConfigurationApi
{
public class AmazonQldbProviderTableConfigurator : IAmazonQldbProviderTableConfigurator
{
internal Func<AuditEvent, string> _tableNameBuilder;
internal Setting<string> _tableName;
internal AmazonQldbProviderAttributeConfigurator _attrConfigurator = new AmazonQldbProviderAttributeConfigurator();

public IAmazonQldbProviderAttributeConfigurator Table(string tableName)
{
_tableNameBuilder = _ => tableName;
_tableName = tableName;
return _attrConfigurator;
}

public IAmazonQldbProviderAttributeConfigurator Table(Func<AuditEvent, string> tableNameBuilder)
{
_tableNameBuilder = tableNameBuilder;
_tableName = tableNameBuilder;
return _attrConfigurator;
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/Audit.NET.AmazonQLDB/Providers/AmazonQldbDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class AmazonQldbDataProvider : AuditDataProvider
/// <summary>
/// The table name to use when saving an audit event in the QLDB table.
/// </summary>
public Func<AuditEvent, string> TableNameBuilder { get; set; }
public Setting<string> TableName { get; set; }

/// <summary>
/// Gets or sets the JSON serializer settings.
Expand Down Expand Up @@ -77,8 +77,8 @@ public AmazonQldbDataProvider(Action<IAmazonQldbProviderConfigurator> config)
if (config != null)
{
config.Invoke(amazonQldbProviderConfigurator);
TableNameBuilder = amazonQldbProviderConfigurator._tableConfigurator?._tableNameBuilder;
CustomAttributes = amazonQldbProviderConfigurator._tableConfigurator?._attrConfigurator?._attributes;
TableName = amazonQldbProviderConfigurator._tableConfigurator._tableName;
CustomAttributes = amazonQldbProviderConfigurator._tableConfigurator._attrConfigurator?._attributes;
}
}

Expand Down Expand Up @@ -185,7 +185,7 @@ await driver.Execute(async trx =>

private string GetTableName(AuditEvent auditEvent)
{
return TableNameBuilder?.Invoke(auditEvent) ?? auditEvent.GetType().Name;
return TableName.GetValue(auditEvent) ?? auditEvent.GetType().Name;
}
}
}
Loading

0 comments on commit 7fe33c8

Please sign in to comment.