如何设置AuditLog.CustomData字段?(基于ASP.NET Boilerplate)
AuditLog.CustomData in ASP.NET Boilerplate Hey Bobby, great to hear your auditing setup with [DisableAuditing] and [Audited] is running smoothly! Let's break down the most effective ways to populate the CustomData field with extra context that isn't captured by default.
1. Manual Setting with IAuditLogManager (One-Off Cases)
For adding custom data to specific methods, inject the IAuditLogManager service and access the current audit log directly in your business logic. This works best when you need to log method-specific context.
public class YourAppService : ApplicationService { private readonly IAuditLogManager _auditLogManager; public YourAppService(IAuditLogManager auditLogManager) { _auditLogManager = auditLogManager; } public async Task ProcessOrderAsync(int orderId, string specialInstructions) { // Get the active audit log (only exists if the method is audited) var currentAuditLog = await _auditLogManager.CurrentLogAsync(); if (currentAuditLog != null) { // Serialize custom data to JSON (since CustomData is a string field) currentAuditLog.CustomData = JsonConvert.SerializeObject(new { OrderId = orderId, SpecialInstructions = specialInstructions, ProcessedBy = CurrentUser.UserName }); } // Your core business logic here } }
2. Global Customization with AuditLogContributor (Cross-Cutting Context)
If you want to add consistent custom data to all audit logs (like tenant ID, IP address, or user metadata), create a custom AuditLogContributor. This runs automatically before any audit log is saved.
Step 1: Create the Contributor
public class CustomAuditContributor : AuditLogContributor { private readonly ICurrentUser _currentUser; private readonly IHttpContextAccessor _httpContextAccessor; public CustomAuditContributor(ICurrentUser currentUser, IHttpContextAccessor httpContextAccessor) { _currentUser = currentUser; _httpContextAccessor = httpContextAccessor; } public override Task PreSaveAsync(AuditLogSaveContext context) { var auditLog = context.AuditInfo; // Merge existing CustomData with new fields (to avoid overwriting) var customData = string.IsNullOrEmpty(auditLog.CustomData) ? new JObject() : JsonConvert.DeserializeObject<JObject>(auditLog.CustomData); // Add global context fields customData["TenantId"] = _currentUser.TenantId; customData["RemoteIp"] = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString(); customData["TimestampUtc"] = DateTime.UtcNow.ToString("o"); // Update the audit log's CustomData auditLog.CustomData = customData.ToString(); return base.PreSaveAsync(context); } }
Step 2: Register the Contributor
Add it to your module's PreInitialize method:
[DependsOn(typeof(AbpAuditingModule))] public class YourProjectModule : AbpModule { public override void PreInitialize() { // Register your custom audit contributor Configuration.Auditing.LogContributors.Add<CustomAuditContributor>(); } }
3. Action Filter for Request-Specific Data (MVC/Web API)
For MVC or Web API controllers, use a custom action filter to capture request parameters, headers, or other HTTP-specific context.
public class AuditCustomDataFilter : ActionFilterAttribute { private readonly IAuditLogManager _auditLogManager; public AuditCustomDataFilter(IAuditLogManager auditLogManager) { _auditLogManager = auditLogManager; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var auditLog = await _auditLogManager.CurrentLogAsync(); if (auditLog != null) { // Capture action arguments and request headers auditLog.CustomData = JsonConvert.SerializeObject(new { ActionParameters = context.ActionArguments, RequestHeaders = context.HttpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()) }); } await next(); } }
Register the filter globally or apply it to specific controllers/actions:
// Global registration in your module public override void PostInitialize() { var filters = IocManager.Resolve<IActionFilterManager>(); filters.AddFilter<AuditCustomDataFilter>(); }
Key Notes
CustomDatais a string field, so always serialize complex objects to JSON for easy parsing later.- When merging data (like in the contributor example), use
JObjectto avoid overwriting existing custom data set elsewhere. CurrentLogAsync()returnsnullfor methods marked with[DisableAuditing], so always add a null check.
Pick the approach that fits your needs: manual setting for one-off method context, a contributor for global cross-cutting data, or an action filter for HTTP-specific details.
内容的提问来源于stack exchange,提问作者Bobby

