feat: 搭建完成审计日志模块

This commit is contained in:
陈淳
2024-01-23 11:52:49 +08:00
parent ba9ac0fa5d
commit 96ae77e5ab
14 changed files with 255 additions and 325 deletions

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Http;
using Volo.Abp.Json;
using Yi.Framework.AuditLogging.Domain.Entities;
namespace Yi.Framework.AuditLogging.Domain;
public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; }
protected IJsonSerializer JsonSerializer { get; }
protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; }
public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator, IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer, IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions)
{
GuidGenerator = guidGenerator;
ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter;
JsonSerializer = jsonSerializer;
ExceptionHandlingOptions = exceptionHandlingOptions.Value;
}
public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo)
{
var auditLogId = GuidGenerator.Create();
var extraProperties = new ExtraPropertyDictionary();
if (auditLogInfo.ExtraProperties != null)
{
foreach (var pair in auditLogInfo.ExtraProperties)
{
extraProperties.Add(pair.Key, pair.Value);
}
}
var entityChanges = auditLogInfo
.EntityChanges?
.Select(entityChangeInfo => new EntityChangeEntity(GuidGenerator, auditLogId, entityChangeInfo, tenantId: auditLogInfo.TenantId))
.ToList()
?? new List<EntityChangeEntity>();
var actions = auditLogInfo
.Actions?
.Select(auditLogActionInfo => new AuditLogActionEntity(GuidGenerator.Create(), auditLogId, auditLogActionInfo, tenantId: auditLogInfo.TenantId))
.ToList()
?? new List<AuditLogActionEntity>();
var remoteServiceErrorInfos = auditLogInfo.Exceptions?.Select(exception => ExceptionToErrorInfoConverter.Convert(exception, options =>
{
options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients;
options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients;
}))
?? new List<RemoteServiceErrorInfo>();
var exceptions = remoteServiceErrorInfos.Any()
? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true)
: null;
var comments = auditLogInfo
.Comments?
.JoinAsString(Environment.NewLine);
var auditLog = new AuditLogAggregateRoot(
auditLogId,
auditLogInfo.ApplicationName,
auditLogInfo.TenantId,
auditLogInfo.TenantName,
auditLogInfo.UserId,
auditLogInfo.UserName,
auditLogInfo.ExecutionTime,
auditLogInfo.ExecutionDuration,
auditLogInfo.ClientIpAddress,
auditLogInfo.ClientName,
auditLogInfo.ClientId,
auditLogInfo.CorrelationId,
auditLogInfo.BrowserInfo,
auditLogInfo.HttpMethod,
auditLogInfo.Url,
auditLogInfo.HttpStatusCode,
auditLogInfo.ImpersonatorUserId,
auditLogInfo.ImpersonatorUserName,
auditLogInfo.ImpersonatorTenantId,
auditLogInfo.ImpersonatorTenantName,
extraProperties,
entityChanges,
actions,
exceptions,
comments
);
return Task.FromResult(auditLog);
}
}

View File

@@ -0,0 +1,61 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
using Yi.Framework.AuditLogging.Domain.Repositories;
using Yi.Framework.Core.Helper;
namespace Yi.Framework.AuditLogging.Domain;
public class AuditingStore : IAuditingStore, ITransientDependency
{
public ILogger<AuditingStore> Logger { get; set; }
protected IAuditLogRepository AuditLogRepository { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected AbpAuditingOptions Options { get; }
protected IAuditLogInfoToAuditLogConverter Converter { get; }
public AuditingStore(
IAuditLogRepository auditLogRepository,
IUnitOfWorkManager unitOfWorkManager,
IOptions<AbpAuditingOptions> options,
IAuditLogInfoToAuditLogConverter converter)
{
AuditLogRepository = auditLogRepository;
UnitOfWorkManager = unitOfWorkManager;
Converter = converter;
Options = options.Value;
Logger = NullLogger<AuditingStore>.Instance;
}
public virtual async Task SaveAsync(AuditLogInfo auditInfo)
{
if (!Options.HideErrors)
{
await SaveLogAsync(auditInfo);
return;
}
try
{
await SaveLogAsync(auditInfo);
}
catch (Exception ex)
{
Logger.LogWarning("Could not save the audit log object: " + Environment.NewLine + auditInfo.ToString());
Logger.LogException(ex, LogLevel.Error);
}
}
protected virtual async Task SaveLogAsync(AuditLogInfo auditInfo)
{
Logger.LogDebug("Yi-请求追踪:" + JsonHelper.ObjToStr(auditInfo, "yyyy-MM-dd HH:mm:ss"));
using (var uow = UnitOfWorkManager.Begin(true))
{
await AuditLogRepository.InsertAsync(await Converter.ConvertAsync(auditInfo));
await uow.CompleteAsync();
}
}
}

View File

@@ -0,0 +1,10 @@
using Yi.Framework.AuditLogging.Domain.Entities;
namespace Yi.Framework.AuditLogging.Domain;
public class EntityChangeWithUsername
{
public EntityChangeEntity EntityChange { get; set; }
public string UserName { get; set; }
}

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Yi.Framework.AuditLogging.Domain.Entities;
namespace Yi.Framework.AuditLogging.Domain;
public interface IAuditLogInfoToAuditLogConverter
{
Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo);
}

View File

@@ -0,0 +1,19 @@
using System.Net;
using Volo.Abp.Auditing;
using Yi.Framework.AuditLogging.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.AuditLogging.Domain.Repositories
{
public interface IAuditLogRepository : ISqlSugarRepository<AuditLogAggregateRoot, Guid>
{
Task<Dictionary<DateTime, double>> GetAverageExecutionDurationPerDayAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default);
Task<long> GetCountAsync(DateTime? startTime = null, DateTime? endTime = null, string httpMethod = null, string url = null, Guid? userId = null, string userName = null, string applicationName = null, string clientIpAddress = null, string correlationId = null, int? maxExecutionDuration = null, int? minExecutionDuration = null, bool? hasException = null, HttpStatusCode? httpStatusCode = null, CancellationToken cancellationToken = default);
Task<EntityChangeEntity> GetEntityChange(Guid entityChangeId, CancellationToken cancellationToken = default);
Task<long> GetEntityChangeCountAsync(Guid? auditLogId = null, DateTime? startTime = null, DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, string entityTypeFullName = null, CancellationToken cancellationToken = default);
Task<List<EntityChangeEntity>> GetEntityChangeListAsync(string sorting = null, int maxResultCount = 50, int skipCount = 0, Guid? auditLogId = null, DateTime? startTime = null, DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, string entityTypeFullName = null, bool includeDetails = false, CancellationToken cancellationToken = default);
Task<List<EntityChangeWithUsername>> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName, CancellationToken cancellationToken = default);
Task<EntityChangeWithUsername> GetEntityChangeWithUsernameAsync(Guid entityChangeId);
Task<List<AuditLogAggregateRoot>> GetListAsync(string sorting = null, int maxResultCount = 50, int skipCount = 0, DateTime? startTime = null, DateTime? endTime = null, string httpMethod = null, string url = null, Guid? userId = null, string userName = null, string applicationName = null, string clientIpAddress = null, string correlationId = null, int? maxExecutionDuration = null, int? minExecutionDuration = null, bool? hasException = null, HttpStatusCode? httpStatusCode = null, bool includeDetails = false);
}
}

View File

@@ -12,6 +12,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj" />
<ProjectReference Include="..\Yi.Framework.AuditLogging.Domain.Shared\Yi.Framework.AuditLogging.Domain.Shared.csproj" />
</ItemGroup>

View File

@@ -1,7 +1,13 @@
namespace Yi.Framework.AuditLogging.Domain
{
public class YiFrameworkAuditLoggingDomainModule
{
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace Yi.Framework.AuditLogging.Domain
{
public class YiFrameworkAuditLoggingDomainModule:AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
}