-
-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exception handling #372
Comments
Could you elaborate a little bit more about your use case? which extensions are you using? what are you logging? And what exactly means 'any errors'? Errors when generating the audit, errors saving the audit? inherent errors of the audited process? |
i'm using Audit.Net, Audit.WebApi.Core and Audit.NET.SqlServer. It would be best if I could set custom error handling on the middleware - eg pass an action, delegate or function, in which I could save the exception to the logs and continue process, or throw it higher and break current process (default behavior). For Audit.NET.SqlServer I would like log and set another provider when error. I know that i can use decorator or add some custom provider, but i think it will be nice feature in your library. |
Not sure If I understand you, but you can have a different middleware to do the exception logging task, I don't see why it should be audit middleware responsibility. Actually the web api audit middleware does log the exception to the audit event. You should explain a little bit more and add some code examples of what do you expect |
Probably i described it not enough precisely.
similar for Audit.WebApi.Core and middleware - set some behavior - when some problem occurrs it shouldn't affect the value returned by api. |
I'm not sure if the library should provide an opinionated mechanism to handle exceptions. Actually I think it's a better idea to write a custom data provider wrapping a real data provider and adding the logic for Fallback. Furthermore, you could use a framework like Polly that provides patterns for resilience (Timeout, Retry, Fallback, Circuit Breaker and so on...). Check this generic data provider configurable via Polly Policies: public class PollyDataProvider : AuditDataProvider
{
private readonly Policy<object> _policy;
private readonly AsyncPolicy<object> _asyncPolicy;
private readonly AuditDataProvider _innerProvider;
public PollyDataProvider(Policy<object> policy, AsyncPolicy<object> asyncPolicy, AuditDataProvider innerDataProvider)
{
_policy = policy;
_asyncPolicy = asyncPolicy;
_innerProvider = innerDataProvider;
}
public override object InsertEvent(AuditEvent auditEvent)
{
var contextData = new Dictionary<string, object>() { { "event", auditEvent } };
return _policy.Execute(ctx => _innerProvider.InsertEvent(auditEvent), contextData);
}
public async override Task<object> InsertEventAsync(AuditEvent auditEvent)
{
var contextData = new Dictionary<string, object>() { { "event", auditEvent } };
return await _asyncPolicy.ExecuteAsync(async ctx => await _innerProvider.InsertEventAsync(auditEvent), contextData);
}
public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
{
var contextData = new Dictionary<string, object>() { { "id", eventId }, { "event", auditEvent } };
_policy.Execute(ctx => { _innerProvider.ReplaceEvent(eventId, auditEvent); return null; }, contextData);
}
public async override Task ReplaceEventAsync(object eventId, AuditEvent auditEvent)
{
var contextData = new Dictionary<string, object>() { { "id", eventId }, { "event", auditEvent } };
await _asyncPolicy.ExecuteAsync(async (ctx) => { await _innerProvider.ReplaceEventAsync(eventId, auditEvent); return null; }, contextData);
}
} Notes:
For example to set up a simple fallback mechanism like the one you are looking for (if a SQL data provider fails, then use the logger): var sqlDataProvider = new Audit.SqlServer.Providers.SqlDataProvider(config => config
.ConnectionString("...")
.TableName("Audit")
.IdColumnName("Id")
.JsonColumnName("Data"));
var fallbackPolicy = Policy<object>.Handle<Exception>().Fallback(
fallbackAction: (context) =>
{
var auditEvent = context["event"] as AuditEvent;
logger.Log(auditEvent.ToJson());
return -1;
},
onFallback: (ex, ctx) => { });
var fallbackAsyncPolicy = Policy<object>.Handle<Exception>().FallbackAsync(
fallbackAction: (Context context, CancellationToken ct) =>
{
var auditEvent = context["event"] as AuditEvent;
logger.Log(auditEvent.ToJson());
return Task.FromResult((object)-1);
},
onFallbackAsync: async (r, c) => await Task.CompletedTask);
Audit.Core.Configuration.Setup()
.UseCustomProvider(new PollyDataProvider(fallbackPolicy, fallbackAsyncPolicy, sqlDataProvider))
.WithCreationPolicy(EventCreationPolicy.InsertOnEnd); |
Or the cheap alternative... public class LoggerFallbackSqlDataProvider : SqlDataProvider
{
public LoggerFallbackSqlDataProvider(Action<ISqlServerProviderConfigurator> config) : base(config) { }
// ...
public override object InsertEvent(AuditEvent auditEvent)
{
try
{
return base.InsertEvent(auditEvent);
}
catch (Exception)
{
logger.Log(auditEvent.ToJson());
return -1;
}
}
public override Task<object> InsertEventAsync(AuditEvent auditEvent)
{
// the same
}
//...
} |
Note that starting in version 24, a new Audit.NET.Polly extension is provided to facilitate the configuration of Polly resilience strategies to any Data Provider within Audit.NET. For example: Audit.Core.Configuration.Setup()
.UsePolly(p => p
.DataProvider(new MongoDataProvider(mongo => mongo...))
.WithResilience(r => r
.AddFallback(new()
{
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
OnFallback = arguments =>
{
var auditEvent = arguments.Context.GetAuditEvent();
_logger.Log(auditEvent.ToJson());
return default;
},
FallbackAction = arguments => default
}))); Or to fallback to a different data provider: Audit.Core.Configuration.Setup()
.UsePolly(p => p
.DataProvider(mongoDataProvider)
.WithResilience(r => r
.AddFallback(new()
{
ShouldHandle = new PredicateBuilder().Handle<Exception>(),
FallbackAction = arguments => arguments.FallbackToDataProvider(fileDataProvider)
}))) |
Is it possible to add an exception handling configuration? Currently, if something doesn't work, it affects the process in the application. It would be good to be able to configure it so that any errors would not affect the process, e.g. handling the request.
The text was updated successfully, but these errors were encountered: