Audit Logging
Implement audit trails for critical actions in under 5 minutes.
1. Install Package
dotnet add package PrimusSaaS.Logging
2. Setup in Program.cs
using PrimusSaaS.Logging.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add Primus Logging with audit support
builder.Logging.ClearProviders();
builder.Logging.AddPrimus(builder.Configuration.GetSection("PrimusLogging"));
var app = builder.Build();
// Add correlation ID middleware
app.UsePrimusLogging();
app.MapControllers();
app.Run();
3. Configure appsettings.json
{
"PrimusLogging": {
"ApplicationId": "my-api",
"Environment": "Production",
"MinLevel": 1,
"Targets": [
{ "Type": "console", "Pretty": true },
{
"Type": "file",
"Path": "logs/audit-.log",
"Async": true,
"MaxFileSize": 52428800,
"MaxRetainedFiles": 30,
"CompressRotatedFiles": true
}
],
"Pii": {
"MaskEmails": true,
"MaskSSN": true,
"MaskCreditCards": true
},
"Audit": {
"Enabled": true,
"IncludeRequestBody": false,
"IncludeResponseBody": false,
"SensitiveEndpoints": ["/auth/*", "/admin/*", "/api/users/*"]
}
}
}
Audit configuration options
| Option | Type | Default | Description |
|---|---|---|---|
Enabled | bool | true | Enable audit logging |
IncludeRequestBody | bool | false | Log request bodies (use with caution) |
IncludeResponseBody | bool | false | Log response bodies |
SensitiveEndpoints | array | [] | Endpoints to always audit |
ExcludedEndpoints | array | ["/health", "/metrics"] | Endpoints to never audit |
4. Use Audit Logging
Extension Method (Recommended)
using PrimusSaaS.Logging.Extensions;
public class AccountService
{
private readonly ILogger<AccountService> _logger;
public AccountService(ILogger<AccountService> logger)
{
_logger = logger;
}
public async Task<bool> UpdateUserRoleAsync(string userId, string newRole, string changedBy)
{
// Log the audit event
await _logger.LogAuditAsync("UserRoleChanged", new
{
UserId = userId,
NewRole = newRole,
ChangedBy = changedBy,
Timestamp = DateTime.UtcNow
});
// Perform the actual update
return true;
}
}
Structured Audit Events
public class OrderController : ControllerBase
{
private readonly ILogger<OrderController> _logger;
public OrderController(ILogger<OrderController> logger)
{
_logger = logger;
}
[HttpPost]
[Authorize]
public async Task<IActionResult> CreateOrder([FromBody] CreateOrderRequest request)
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var orderId = Guid.NewGuid().ToString();
// Audit: Order Created
await _logger.LogAuditAsync("OrderCreated", new
{
OrderId = orderId,
UserId = userId,
Amount = request.Amount,
ItemCount = request.Items.Count,
IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString()
});
return Created($"/orders/{orderId}", new { orderId });
}
[HttpDelete("{id}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> DeleteOrder(string id)
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
// Audit: Order Deleted (sensitive action)
await _logger.LogAuditAsync("OrderDeleted", new
{
OrderId = id,
DeletedBy = userId,
Reason = "Admin request",
IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString()
});
return NoContent();
}
}
5. Test It
# Make a request that triggers audit logging
curl -X POST http://localhost:5001/api/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"amount": 99.99, "items": ["item1", "item2"]}'
# Check audit log file
cat logs/audit-*.log | jq .
# Expected output:
# {
# "eventType": "OrderCreated",
# "timestamp": "2024-01-15T10:30:45Z",
# "data": {
# "orderId": "abc-123",
# "userId": "user-456",
# "amount": 99.99,
# "itemCount": 2,
# "ipAddress": "127.0.0.1"
# }
# }
Audit Event Types
| Event Type | When to Use | Example Properties |
|---|---|---|
UserLogin | Authentication success | UserId, IpAddress, Method |
UserLogout | Session termination | UserId, SessionDuration |
PasswordChanged | Credential update | UserId, ChangedBy |
RoleChanged | Permission modification | UserId, OldRole, NewRole |
DataAccessed | Sensitive data read | UserId, ResourceType, ResourceId |
DataModified | Sensitive data change | UserId, ResourceType, ChangeType |
DataDeleted | Data removal | UserId, ResourceType, ResourceId |
Compliance Considerations
SOC 2 / HIPAA / GDPR
- Always include: Who (userId), What (action), When (timestamp), Where (IP)
- Enable
MaskPiifor personal data protection - Set appropriate retention (30-90 days minimum)
- Use immutable storage (append-only files or database)
These patterns can support compliance programs, but they are not a certification. Validate requirements with your legal/compliance team.
Next Steps
| Want to... | See Guide |
|---|---|
| Configure log targets | Console |
| Add OpenTelemetry traces | Observability |
| Set up role-based access | RBAC |