The IApiLoggingService interface provides methods for manual logging, session management, and GDPR compliance operations.
Injecting the Service
public class MyController : ControllerBase
{
private readonly IApiLoggingService _loggingService;
public MyController(IApiLoggingService loggingService)
{
_loggingService = loggingService;
}
}Methods
StartSessionAsync
Manually start a new user session. Returns the session ID.
Task<string> StartSessionAsync(
string userId,
SessionMetadata metadata,
CancellationToken ct = default);
// Usage
var sessionId = await _loggingService.StartSessionAsync(
userId: "user-123",
metadata: new SessionMetadata
{
TenantId = "tenant-abc",
IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(),
UserAgent = Request.Headers["User-Agent"].ToString()
});EndSessionAsync
End an active session with a reason code.
Task EndSessionAsync(
string sessionId,
SessionEndReason reason,
CancellationToken ct = default);
// Usage
await _loggingService.EndSessionAsync(
sessionId: "session-id",
reason: SessionEndReason.Logout);
// Available reasons:
// - SessionEndReason.Logout
// - SessionEndReason.Timeout
// - SessionEndReason.TokenExpired
// - SessionEndReason.ManualClose
// - SessionEndReason.SystemShutdownGetOrCreateSessionAsync
Get the current session ID or create a new session based on the HTTP context. Used internally by the [ApiLogging] attribute.
Task<string?> GetOrCreateSessionAsync(
HttpContext context,
CancellationToken ct = default);
// Usage
var sessionId = await _loggingService.GetOrCreateSessionAsync(HttpContext);
if (sessionId == null)
{
// No user identity found and anonymous sessions disabled
return Unauthorized();
}LogActionAsync
Manually log an API action. The action is enqueued to a background channel for batch processing.
Task LogActionAsync(
ApiAction actionLog,
CancellationToken ct = default);
// Usage
var action = new ApiAction
{
Id = ObjectId.GenerateNewId().ToString(),
SessionId = sessionId,
TimeStamp = DateTime.UtcNow,
EndpointInfo = new EndpointInfo
{
Controller = "OrdersController",
Action = "CreateOrder",
Route = "/api/orders"
},
PayloadInfo = new PayloadInfo
{
Method = "POST",
Body = orderData
},
ResponseInfo = new ResponseInfo
{
StatusCode = 201,
Duration = TimeSpan.FromMilliseconds(45)
}
};
await _loggingService.LogActionAsync(action);AddLogMessageAsync
Add a log message to an in-flight action (before the request completes). The action ID is stored in HttpContext.Items["ApiLoggingActionId"].
Task AddLogMessageAsync(
string actionId,
string message,
CancellationToken ct = default);
// Usage in a controller action
[HttpPost]
[ApiLogging]
public async Task<IActionResult> ProcessOrder(OrderDto order)
{
var actionId = HttpContext.Items["ApiLoggingActionId"]?.ToString();
await _loggingService.AddLogMessageAsync(actionId, "Starting order validation");
var validationResult = await ValidateOrder(order);
await _loggingService.AddLogMessageAsync(actionId, $"Validation complete: {validationResult.Status}");
await _loggingService.AddLogMessageAsync(actionId, "Processing payment");
var paymentResult = await ProcessPayment(order);
await _loggingService.AddLogMessageAsync(actionId, $"Payment processed: {paymentResult.TransactionId}");
return Ok(new { OrderId = order.Id, TransactionId = paymentResult.TransactionId });
}DeleteUserDataAsync
Delete all logging data for a user (GDPR compliance).
Task DeleteUserDataAsync(
string userId,
string tenantId,
CancellationToken ct = default);
// Usage
[HttpDelete("users/{userId}/data")]
public async Task<IActionResult> DeleteUserData(string userId)
{
var tenantId = User.FindFirst("tenantId")?.Value ?? "default";
await _loggingService.DeleteUserDataAsync(userId, tenantId);
return NoContent();
}ExportUserDataStreamAsync
Export all user data in a streaming fashion for GDPR data portability requests. Returns an IAsyncEnumerable of pages.
IAsyncEnumerable<UserDataExportPage> ExportUserDataStreamAsync(
string userId,
int pageSize = 1000,
CancellationToken ct = default);
// Usage
[HttpGet("users/{userId}/export")]
public async IAsyncEnumerable<UserDataExportPage> ExportUserData(
string userId,
[EnumeratorCancellation] CancellationToken ct)
{
await foreach (var page in _loggingService.ExportUserDataStreamAsync(userId, pageSize: 500, ct))
{
if (!page.Success)
{
_logger.LogError("Export error: {Error}", page.ErrorMessage);
yield break;
}
yield return page;
}
}Data Models
ApiAction
public class ApiAction
{
public string Id { get; set; }
public string SessionId { get; set; }
public string? UserId { get; set; }
public string? TenantId { get; set; }
public DateTime TimeStamp { get; set; }
// Tracing
public string? CorrelationId { get; set; }
public string? IdempotencyKey { get; set; }
public string? TraceId { get; set; }
public string? SpanId { get; set; }
public string? ParentSpanId { get; set; }
// Request info
public RequesterInfo? RequesterInfo { get; set; }
public PayloadInfo? PayloadInfo { get; set; }
public EndpointInfo? EndpointInfo { get; set; }
// Response info
public ResponseInfo? ResponseInfo { get; set; }
public List<string> LogMessages { get; set; }
// Metadata
public string? MachineName { get; set; }
public string? EnvironmentName { get; set; }
public string? ApiVersion { get; set; }
public Dictionary<string, object>? CustomData { get; set; }
}SessionMetadata
public class SessionMetadata
{
public string? TenantId { get; set; }
public string? IpAddress { get; set; }
public string? UserAgent { get; set; }
public string? DeviceId { get; set; }
public Dictionary<string, object>? CustomData { get; set; }
}UserDataExportPage
public class UserDataExportPage
{
public bool Success { get; set; }
public string? ErrorMessage { get; set; }
public int PageNumber { get; set; }
public bool HasMore { get; set; }
// Only populated on first page
public List<LoginSession>? Sessions { get; set; }
// Populated on every page
public List<ApiAction> Actions { get; set; }
}Best Practices
- Use
AddLogMessageAsyncto add context to long-running operations - Store the action ID from
HttpContext.Items["ApiLoggingActionId"] - For GDPR exports, use streaming to handle large datasets
- Always specify the tenant ID for multi-tenant applications
