How to Search Audit Data in Audit.Net? #692
Replies: 6 comments 35 replies
-
The command interceptor output is the SQL generated internally by the Entity Framework, making it nearly impossible to parse without tightly coupling it with the EF code. Consider the issue from a different perspective: if you control the code responsible for generating the query, you could implement a correlated log within that code. Is this an ASP.NET API? |
Beta Was this translation helpful? Give feedback.
-
Yes, that's exactly what I was suggesting—creating an additional audit event. However, the challenge lies in correlating the manually created log with the EF log. If you were using MVC, this could be easily accomplished by leveraging the IHttpContextAccessor to attach the TraceId to all audit events via a custom action. I will investigate further and let you know. |
Beta Was this translation helpful? Give feedback.
-
Yes, it makes sense to me to log the user's action (searching for students) separately from the database query and other events. There are several ways to correlate these events:
With Activity Trace enabled, you'll see that events generated from the same Blazor circuit share the same For example: // On your startup logic:
// Include the activity trace
Audit.Core.Configuration.IncludeActivityTrace = true;
// Make sure there is one listener
ActivitySource.AddActivityListener(new ActivityListener()
{
ShouldListenTo = f => true,
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData
});
You can then use a Custom Action in the For example: public class AuditedCircuitHandler : CircuitHandler
{
private readonly ICircuitAccessor _circuitAccessor;
public AuditedCircuitHandler(ICircuitAccessor circuitAccessor)
{
this._circuitAccessor = circuitAccessor;
}
public override async Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
{
_circuitAccessor.CurrentCircuit = circuit;
await LogAsync("Opened", circuit);
await base.OnCircuitOpenedAsync(circuit, cancellationToken);
}
public override async Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
{
await LogAsync("Closed", circuit);
await base.OnCircuitClosedAsync(circuit, cancellationToken);
}
private Task LogAsync(string action, Circuit circuit)
{
// Log the circuit action
return Audit.Core.AuditScope.LogAsync($"CIRCUIT_{action}", new { CircuitId = circuit.Id, Action = action });
}
}
public interface ICircuitAccessor
{
Circuit CurrentCircuit { get; set; }
}
public class CircuitAccessor : ICircuitAccessor
{
public Circuit? CurrentCircuit { get; set; }
} In your startup logic, register the circuit handler and accessor as Scoped, and set up a custom action to retrieve the scoped accessor using the DbContext GetService extension builder.Services.AddScoped<ICircuitAccessor, CircuitAccessor>();
builder.Services.AddScoped<CircuitHandler, AuditedCircuitHandler>();
Audit.Core.Configuration.AddOnCreatedAction(scope =>
{
var cmd = scope.GetCommandEntityFrameworkEvent();
if (cmd != null)
{
var circuitAccessor = cmd.GetDbContext().GetService<ICircuitAccessor>();
scope.SetCustomField("CircuitId", circuitAccessor.CurrentCircuit.Id);
}
}); |
Beta Was this translation helpful? Give feedback.
-
Given that you're using the latest version, a better approach would be to register a custom This eliminates the need for the Custom public class CustomAuditScopeFactory : AuditScopeFactory
{
private readonly ICircuitAccessor _circuitAccessor;
public CustomAuditScopeFactory(ICircuitAccessor circuitAccessor)
{
_circuitAccessor = circuitAccessor;
}
public override void OnScopeCreated(AuditScope auditScope)
{
auditScope.SetCustomField("CircuitContext", _circuitAccessor);
}
}
// And in your start-up:
builder.Services.AddScoped<IAuditScopeFactory, CustomAuditScopeFactory>(); I'm attaching a working sample Blazor project using EF Core, featuring a custom audit circuit handler, circuit accessor, and a custom audit scope factory, all configured to log circuit context information into the audit events. The project is built upon the default Blazor template and includes the following files:
Once compiled and run, you should be able to register a new user, confirm the registration, log in, navigate to the Search page, and perform a search, which will be logged accordingly You should see the logs being generated in your "C:\Logs" folder: Sample project: |
Beta Was this translation helpful? Give feedback.
-
I have a question about the new
I noticed that the json it does not have a "database" property like all the other json files have. Do I need to pass that in myself always? Or am I missing a setting? If I want to also store the "query" results just the EF_Command I am guessing I am going to have to first do the actual query and then after do the Finally, I see the resulted json is like this
I am guessing if I add another property that it will be at this level too.
If I wanted to have a wrapper around this, I would need to do it myself, right?
|
Beta Was this translation helpful? Give feedback.
-
Events manually created using AuditFactory.Create(c => c
.EventType("Search")
.ExtraFields(new
{
SearchParams = new
{
SearchText = "test",
Age = 20
}
})); |
Beta Was this translation helpful? Give feedback.
-
Hi
I am looking if Audit.NET can help me out. It seems pretty powerful, but I am having problems on how after I get the data "logged" how to actually efficiently search through it.
My requirements are this
Basically the last one is the one that is tripping me up, as something like first name would be stored in the JSON data which gets kind of hard and slow to do searched on in SQL Server even with the json improvements they made. I could make a computed value but then I am basically needed to do this on every single column from every single table as in that database as everything pretty much needs to be searchable (lastname, address, city, country, etc)
I found this post from a few year ago in Stack: https://stackoverflow.com/questions/68128212/how-to-achieve-generic-audit-net-json-data-processing
So it seems I might be using the wrong tool for the job and maybe this CosmosDB will help, but I am not sure if it will solve my problem still.
The example given was for changes to like "address"" what is great but like I said I need to look at "selects as well"
When I look at what is being stored in my sql database via the AuditCommandInterceptor I see something like
Now even if I am using CosmosDB how the heck do I search for and find records that had a search of "Jim" . I know I can filter down by "ExectureReader" but after that I am lost. I don't know if I have to somehow search down to "parameters" property and then somehow just look for "jim" as the value and ignore this "@__i_0: " or what.
The kicker is even if I did know the property name like in the "stack" example of they look for "Address" well my audit log going to have many different applications logs in it every time we add a new application it will be in this log as requirement is to search all our applications and these applications may have different databases.
So just imagine that there are 2 applications and when need to find jim. Well maybe in application 1 the data of "name" is being stored under a column called "First_Name" and the other one "First".
How could I do a search that still fast and find all records of "jim" I can't keep track of all the different column names.
Beta Was this translation helpful? Give feedback.
All reactions