@@ -0,0 +1,71 @@
|
||||
namespace ROLAC.API.Entities.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// An append-only audit row recording a meaningful action: a data change (Create/Update/
|
||||
/// Delete with before→after values), a security event (login, role/permission change), or a
|
||||
/// key business action (check issued, expense approved, ...). Does NOT inherit AuditableEntity.
|
||||
/// </summary>
|
||||
public class AuditLog
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public LogLevelEnum Level { get; set; } = LogLevelEnum.Information;
|
||||
|
||||
/// <summary>One of <see cref="AuditActions"/>.</summary>
|
||||
public string Action { get; set; } = null!;
|
||||
|
||||
/// <summary>One of <see cref="AuditCategories"/> — drives the UI grouping.</summary>
|
||||
public string Category { get; set; } = null!;
|
||||
|
||||
public string? EntityName { get; set; }
|
||||
|
||||
/// <summary>String to cover int, Guid and string primary keys uniformly.</summary>
|
||||
public string? EntityId { get; set; }
|
||||
|
||||
/// <summary>JSON <c>{ "before": {...}, "after": {...} }</c> (jsonb column); sensitive fields masked.</summary>
|
||||
public string? Changes { get; set; }
|
||||
|
||||
/// <summary>Human-readable one-liner, e.g. "Check #1042 issued to Acme — $1,200.00".</summary>
|
||||
public string? Summary { get; set; }
|
||||
|
||||
public string? UserId { get; set; }
|
||||
/// <summary>Denormalized actor email — survives user deletion and avoids a join in the grid.</summary>
|
||||
public string? UserEmail { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
public string? CorrelationId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Canonical audit action names (stored verbatim in <see cref="AuditLog.Action"/>).</summary>
|
||||
public static class AuditActions
|
||||
{
|
||||
public const string Create = "Create";
|
||||
public const string Update = "Update";
|
||||
public const string Delete = "Delete";
|
||||
public const string Login = "Login";
|
||||
public const string Logout = "Logout";
|
||||
public const string LoginFailed = "LoginFailed";
|
||||
public const string RoleChanged = "RoleChanged";
|
||||
public const string UserDeactivated = "UserDeactivated";
|
||||
public const string PermissionChanged = "PermissionChanged";
|
||||
public const string CheckIssued = "CheckIssued";
|
||||
public const string CheckVoided = "CheckVoided";
|
||||
public const string ExpenseApproved = "ExpenseApproved";
|
||||
public const string StatementFinalized = "StatementFinalized";
|
||||
|
||||
public static readonly IReadOnlyList<string> All =
|
||||
[
|
||||
Create, Update, Delete, Login, Logout, LoginFailed, RoleChanged,
|
||||
UserDeactivated, PermissionChanged, CheckIssued, CheckVoided,
|
||||
ExpenseApproved, StatementFinalized,
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>Top-level audit grouping (stored verbatim in <see cref="AuditLog.Category"/>).</summary>
|
||||
public static class AuditCategories
|
||||
{
|
||||
public const string DataChange = "DataChange";
|
||||
public const string Security = "Security";
|
||||
public const string Business = "Business";
|
||||
|
||||
public static readonly IReadOnlyList<string> All = [DataChange, Security, Business];
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using MsLogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
|
||||
namespace ROLAC.API.Entities.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Persisted severity for system and audit logs. Byte-backed so it stores compactly
|
||||
/// as <c>smallint</c> and sorts/filters by ordinal. Deliberately omits the
|
||||
/// <see cref="MsLogLevel.None"/> sentinel (value 6) — "None" means "log nothing" and
|
||||
/// is meaningless once a row already exists.
|
||||
/// </summary>
|
||||
public enum LogLevelEnum : byte
|
||||
{
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Information = 2,
|
||||
Warning = 3,
|
||||
Error = 4,
|
||||
Critical = 5,
|
||||
}
|
||||
|
||||
public static class LogLevelMap
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a framework <see cref="MsLogLevel"/> to our persisted enum.
|
||||
/// <see cref="MsLogLevel.None"/> falls through to <see cref="LogLevelEnum.Critical"/>
|
||||
/// (it never reaches the sink because the floor filter drops it first).
|
||||
/// </summary>
|
||||
public static LogLevelEnum FromMs(MsLogLevel level) => level switch
|
||||
{
|
||||
MsLogLevel.Trace => LogLevelEnum.Trace,
|
||||
MsLogLevel.Debug => LogLevelEnum.Debug,
|
||||
MsLogLevel.Information => LogLevelEnum.Information,
|
||||
MsLogLevel.Warning => LogLevelEnum.Warning,
|
||||
MsLogLevel.Error => LogLevelEnum.Error,
|
||||
MsLogLevel.Critical => LogLevelEnum.Critical,
|
||||
_ => LogLevelEnum.Critical,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace ROLAC.API.Entities.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// An append-only operational log row — one per persisted framework/app log event,
|
||||
/// including every unhandled API exception captured by ExceptionHandlingMiddleware.
|
||||
/// Intentionally does NOT inherit AuditableEntity: these rows are never updated and
|
||||
/// must not be re-stamped or re-audited (that would recurse through the log pipeline).
|
||||
/// </summary>
|
||||
public class SystemLog
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public LogLevelEnum Level { get; set; }
|
||||
|
||||
/// <summary>The ILogger category (source), e.g. "ROLAC.API.Controllers.GivingsController".</summary>
|
||||
public string Category { get; set; } = null!;
|
||||
public int? EventId { get; set; }
|
||||
public string Message { get; set; } = null!;
|
||||
|
||||
/// <summary>Full <c>exception.ToString()</c> (type + message + stack), when present.</summary>
|
||||
public string? Exception { get; set; }
|
||||
|
||||
public string? RequestPath { get; set; }
|
||||
public string? HttpMethod { get; set; }
|
||||
public int? StatusCode { get; set; }
|
||||
|
||||
/// <summary>The acting user id ("sub" claim), or null for background/system events.</summary>
|
||||
public string? UserId { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
public string? CorrelationId { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user