Innovabit.DotNet.Interceptors.Audit.Abstractions
2.0.117
dotnet add package Innovabit.DotNet.Interceptors.Audit.Abstractions --version 2.0.117
NuGet\Install-Package Innovabit.DotNet.Interceptors.Audit.Abstractions -Version 2.0.117
<PackageReference Include="Innovabit.DotNet.Interceptors.Audit.Abstractions" Version="2.0.117" />
<PackageVersion Include="Innovabit.DotNet.Interceptors.Audit.Abstractions" Version="2.0.117" />
<PackageReference Include="Innovabit.DotNet.Interceptors.Audit.Abstractions" />
paket add Innovabit.DotNet.Interceptors.Audit.Abstractions --version 2.0.117
#r "nuget: Innovabit.DotNet.Interceptors.Audit.Abstractions, 2.0.117"
#:package Innovabit.DotNet.Interceptors.Audit.Abstractions@2.0.117
#addin nuget:?package=Innovabit.DotNet.Interceptors.Audit.Abstractions&version=2.0.117
#tool nuget:?package=Innovabit.DotNet.Interceptors.Audit.Abstractions&version=2.0.117
Innovabit.DotNet.Interceptors.Audit.Abstractions
Business audit trails for Innovabit.DotNet.Interceptors
via an [Audited] attribute. This is the provider-agnostic base: it ships the interceptor, the
attribute, the AuditEntry shape and the IAuditSink abstraction. Pick a sink — or write one — to
decide where entries are kept:
Innovabit.DotNet.Interceptors.Audit.EntityFrameworkCore— one row per entry, in your database.
Audit is not logging
Logging ([Log]) |
Auditing ([Audited]) |
|
|---|---|---|
| Who is it for | You, debugging | Compliance, an investigation, a customer dispute |
| Lifetime | Days or weeks | Years |
| Written when it fails | Usually | Always — an attempt is a fact worth keeping |
| Carries the acting user | No | Yes |
| Where it goes | A log sink | A durable store you can query |
Put [Audited] on the handful of operations someone might one day have to answer for, not on
everything.
Usage
services
.AddInterceptors()
.AddEntityFrameworkCoreAudit<BillingDbContext>() // or .AddAuditSink<MyOwnSink>()
.UseAuditSubjectAccessor<HttpAuditSubjectAccessor>();
services.AddScopedWithInterception<IRefundService, RefundService>();
public class RefundService : IRefundService
{
[Audited(Action = "order.refunded", Category = "finance")]
public Task<Refund> RefundAsync(string orderId, decimal amount, [AuditIgnore] string internalNote)
{ ... }
}
[Audited]
| Property | Meaning |
|---|---|
Action |
Business name of what happened. Defaults to TypeName.MethodName. |
Category |
Optional grouping — a bounded context, a regulation, a subsystem. |
IncludeArguments |
Record the arguments. Defaults to true. |
IncludeResult |
Record the returned value. Defaults to false — results are large and the trail cares about the action. |
Parameters marked [AuditIgnore] are recorded as ***, and CancellationToken parameters are
left out entirely. [AuditIgnore] keeps a parameter out of the audit trail specifically; the
cross-cutting [Sensitive] and [Mask] from the core package keep it out of every record the
suite writes — logs, spans and exception notifications included — and are applied here as well.
Sensitive members inside an argument
The default IAuditSerializer honours [Sensitive] and [Mask] on the properties and fields
of whatever it writes out, so the rules keep working once a value stops being an argument and
becomes an object inside one:
public class Customer
{
public string Name { get; set; }
[Sensitive] public string Email { get; set; }
[Mask(ShowLast = 4)] public string CardNumber { get; set; }
}
[Audited(Action = "order.placed")]
public Task PlaceAsync(Order order) { ... }
The stored row keeps {"Name":"Ada Lovelace","Email":"***","CardNumber":"************1111"},
through nested objects and collections alike, while the method itself still receives the real
object. A serializer of your own opts in with one line — see RedactionJsonModifier in the core
package.
When serialization fails outright, the entry records <unserializable: Type.Name> rather than
ToString(): a record's generated ToString prints every property, marked ones included, which
would turn a serialization failure into exactly the leak the attributes exist to prevent. The redaction is deliberate: an entry that silently dropped an argument would be
indistinguishable from one where it was never passed, which is exactly the ambiguity an audit exists
to remove.
What an entry carries
AuditEntry holds the action and category, the type and method, the arguments, the outcome
(Succeeded, plus ExceptionType/ExceptionMessage on failure), OccurredAt and Duration, the
Subject, and a CorrelationId taken from the ambient Activity — so an entry lines up with the
trace of the request that produced it.
Who did it
Only your application knows where the current user lives, so IAuditSubjectAccessor is the one
thing you have to supply. The default reads Thread.CurrentPrincipal, which ASP.NET Core does not
set — a web application registers its own:
public sealed class HttpAuditSubjectAccessor(IHttpContextAccessor accessor) : IAuditSubjectAccessor
{
public string? GetSubject() => accessor.HttpContext?.User.Identity?.Name;
}
services.AddInterceptors().UseAuditSubjectAccessor<HttpAuditSubjectAccessor>();
An accessor that throws leaves Subject empty rather than failing the operation.
Ordering
The interceptor runs at InterceptorOrders.Audit (50) — just inside logging, outside
validation. That is what makes rejected attempts show up in the trail: a call turned away by
validation, authorization or a business rule is still recorded, because "who tried to do what" is
half of what an audit is for.
A failing sink never fails the call
Whatever a sink throws is swallowed. An audit trail that takes the business operation down with it when the audit store hiccups is worse than one with a gap — so a sink that cannot afford to lose entries should do its own durable buffering rather than rely on the exception surfacing.
Writing a sink
public sealed class FileAuditSink(IAuditSerializer serializer) : IAuditSink
{
public ValueTask WriteAsync(AuditEntry entry, CancellationToken cancellationToken = default) { ... }
public void Write(AuditEntry entry) { ... }
}
services.AddInterceptors().AddAuditSink<FileAuditSink>();
Register as many as you like — every sink gets every entry.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Innovabit.DotNet.Interceptors.Abstractions (>= 2.0.117)
-
net8.0
- Innovabit.DotNet.Interceptors.Abstractions (>= 2.0.117)
- System.Text.Json (>= 10.0.14)
-
net9.0
- Innovabit.DotNet.Interceptors.Abstractions (>= 2.0.117)
- System.Text.Json (>= 10.0.14)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Innovabit.DotNet.Interceptors.Audit.Abstractions:
| Package | Downloads |
|---|---|
|
Innovabit.DotNet.Interceptors.Audit.EntityFrameworkCore
Entity Framework Core sink for interceptor-based audit trails. |
|
|
Innovabit.DotNet.Interceptors.All
Meta-package: every interceptor concern in the suite (logging, validation, caching, notifications, telemetry, audit, authorization, rate limiting, coordination, feature gates, transactions, retry/timeout/circuit breaker/fallback) in one reference. Providers are separate packages — pick the ones you use. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.117 | 44 | 10/31/2026 |
| 2.0.116 | 120 | 10/26/2026 |
| 2.0.115 | 128 | 10/17/2026 |
| 2.0.114 | 130 | 10/10/2026 |
| 2.0.113 | 136 | 10/3/2026 |
| 2.0.112 | 133 | 9/28/2026 |
| 2.0.111 | 128 | 9/28/2026 |
| 2.0.110 | 134 | 9/27/2026 |
| 2.0.109 | 127 | 9/26/2026 |
| 2.0.108 | 128 | 9/26/2026 |
| 2.0.107 | 132 | 9/26/2026 |
| 2.0.106 | 138 | 9/25/2026 |
| 2.0.105 | 122 | 9/25/2026 |
| 1.0.0.104 | 109 | 9/25/2026 |
| 1.0.0.103 | 112 | 9/25/2026 |
| 1.0.0.102 | 113 | 9/25/2026 |
| 1.0.0.101 | 111 | 9/25/2026 |
| 1.0.0.100 | 110 | 9/25/2026 |