From 1df5c7f6bd90354f87bcc0fa7ec71f4785189b58 Mon Sep 17 00:00:00 2001 From: guotianliang <1265346495@qq.com> Date: Thu, 12 May 2022 17:32:31 +0800 Subject: [PATCH] add auditing-logging --- Directory.Build.props | 2 + Sanhe.Abp.Framework.sln | 73 ++++ .../FodyWeavers.xml | 3 + .../README.md | 38 ++ ...anhe.Abp.AuditLogging.Elasticsearch.csproj | 20 + .../AbpAuditLoggingElasticsearchModule.cs | 22 + .../AbpAuditLoggingElasticsearchOptions.cs | 17 + .../AuditLogInfoToAuditLogConverter.cs | 104 +++++ .../ElasticsearchAuditLogManager.cs | 362 +++++++++++++++ .../ElasticsearchEntityChangeStore.cs | 371 ++++++++++++++++ .../ElasticsearchSecurityLogManager.cs | 268 ++++++++++++ .../IAuditLogInfoToAuditLogConverter.cs | 10 + .../Elasticsearch/IIndexInitializer.cs | 9 + .../Elasticsearch/IIndexNameNormalizer.cs | 7 + .../Elasticsearch/IndexInitializer.cs | 107 +++++ .../Elasticsearch/IndexInitializerService.cs | 21 + .../Elasticsearch/IndexNameNormalizer.cs | 30 ++ .../FodyWeavers.xml | 3 + .../README.md | 32 ++ ...bp.AuditLogging.EntityFrameworkCore.csproj | 21 + ...bpAuditLoggingEntityFrameworkCoreModule.cs | 25 ++ .../AbpAuditingMapperProfile.cs | 21 + .../EntityFrameworkCore/AuditLogManager.cs | 179 ++++++++ .../EntityFrameworkCore/SecurityLogManager.cs | 144 ++++++ .../Sanhe.Abp.AuditLogging/FodyWeavers.xml | 3 + .../Sanhe.Abp.AuditLogging.csproj | 17 + .../Abp/AuditLogging/AbpAuditLoggingModule.cs | 15 + .../Sanhe/Abp/AuditLogging/AuditLog.cs | 115 +++++ .../Sanhe/Abp/AuditLogging/AuditLogAction.cs | 47 ++ .../Sanhe/Abp/AuditLogging/AuditingStore.cs | 23 + .../AuditLogging/DefaultAuditLogManager.cs | 96 ++++ .../AuditLogging/DefaultEntityChangeStore.cs | 40 ++ .../AuditLogging/DefaultSecurityLogManager.cs | 87 ++++ .../Sanhe/Abp/AuditLogging/EntityChange.cs | 69 +++ .../AuditLogging/EntityChangeWithUsername.cs | 9 + .../Abp/AuditLogging/EntityPropertyChange.cs | 43 ++ .../Abp/AuditLogging/IAuditLogManager.cs | 64 +++ .../Abp/AuditLogging/IEntityChangeStore.cs | 46 ++ .../Abp/AuditLogging/ISecurityLogManager.cs | 56 +++ .../Sanhe/Abp/AuditLogging/SecurityLog.cs | 72 +++ .../Abp/AuditLogging/SecurityLogStore.cs | 23 + .../FodyWeavers.xml | 3 + ....Abp.Auditing.Application.Contracts.csproj | 28 ++ .../AbpAuditingApplicationContractsModule.cs | 34 ++ .../Auditing/AuditLogs/AuditLogActionDto.cs | 18 + .../Abp/Auditing/AuditLogs/AuditLogDto.cs | 55 +++ .../AuditLogs/AuditLogGetByPagedDto.cs | 24 + .../Abp/Auditing/AuditLogs/EntityChangeDto.cs | 27 ++ .../AuditLogs/EntityChangeGetByPagedDto.cs | 15 + .../EntityChangeGetWithUsernameDto.cs | 7 + .../AuditLogs/EntityChangeWithUsernameDto.cs | 8 + .../AuditLogs/EntityPropertyChangeDto.cs | 16 + .../Auditing/AuditLogs/IAuditLogAppService.cs | 16 + .../AuditLogs/IEntityChangesAppService.cs | 17 + .../Auditing/AuditingRemoteServiceConsts.cs | 7 + .../AuditingFeatureDefinitionProvider.cs | 43 ++ .../Auditing/Features/AuditingFeatureNames.cs | 15 + .../Auditing/Localization/Resources/en.json | 88 ++++ .../Localization/Resources/zh-Hans.json | 88 ++++ .../Sanhe/Abp/Auditing/Logging/Dto/LogDto.cs | 15 + .../Auditing/Logging/Dto/LogExceptionDto.cs | 13 + .../Abp/Auditing/Logging/Dto/LogFieldDto.cs | 21 + .../Auditing/Logging/Dto/LogGetByPagedDto.cs | 23 + .../Abp/Auditing/Logging/ILogAppService.cs | 14 + .../AuditingPermissionDefinitionProvider.cs | 35 ++ .../Permissions/AuditingPermissionNames.cs | 18 + .../SecurityLogs/ISecurityLogAppService.cs | 16 + .../Auditing/SecurityLogs/SecurityLogDto.cs | 30 ++ .../SecurityLogs/SecurityLogGetByPagedDto.cs | 18 + .../FodyWeavers.xml | 3 + .../Sanhe.Abp.Auditing.Application.csproj | 22 + .../Auditing/AbpAuditingApplicationModule.cs | 24 + .../Abp/Auditing/AbpAuditingMapperProfile.cs | 31 ++ .../Auditing/AuditLogs/AuditLogAppService.cs | 63 +++ .../AuditLogs/EntityChangesAppService.cs | 61 +++ .../AuditingApplicationServiceBase.cs | 14 + .../Abp/Auditing/Logging/LogAppService.cs | 49 +++ .../SecurityLogs/SecurityLogAppService.cs | 57 +++ .../FodyWeavers.xml | 3 + .../Sanhe.Abp.Auditing.HttpApi.csproj | 19 + .../Abp/Auditing/AbpAuditingHttpApiModule.cs | 41 ++ .../Auditing/AuditLogs/AuditLogController.cs | 61 +++ .../AuditLogs/EntityChangesController.cs | 49 +++ .../Abp/Auditing/Logging/LogController.cs | 49 +++ .../SecurityLogs/SecurityLogController.cs | 62 +++ .../FodyWeavers.xml | 3 + .../README.md | 32 ++ ...e.Abp.Logging.Serilog.Elasticsearch.csproj | 24 + ...oggingSerilogElasticsearchMapperProfile.cs | 16 + .../AbpLoggingSerilogElasticsearchModule.cs | 31 ++ .../AbpLoggingSerilogElasticsearchOptions.cs | 12 + .../SerilogElasticsearchLoggingManager.cs | 412 ++++++++++++++++++ .../Serilog/Elasticsearch/SerilogException.cs | 26 ++ .../Serilog/Elasticsearch/SerilogField.cs | 58 +++ .../Serilog/Elasticsearch/SerilogInfo.cs | 26 ++ .../logging/Sanhe.Abp.Logging/FodyWeavers.xml | 3 + modules/logging/Sanhe.Abp.Logging/README.md | 15 + .../Sanhe.Abp.Logging.csproj | 14 + .../AbpLoggingEnricherPropertyNames.cs | 8 + .../Sanhe/Abp/Logging/AbpLoggingModule.cs | 15 + .../Abp/Logging/DefaultLoggingManager.cs | 72 +++ .../Sanhe/Abp/Logging/ILoggingManager.cs | 51 +++ .../Sanhe/Abp/Logging/LogException.cs | 13 + .../Sanhe/Abp/Logging/LogField.cs | 21 + .../Sanhe/Abp/Logging/LogInfo.cs | 15 + .../FodyWeavers.xml | 3 + .../README.md | 48 ++ ...e.Abp.Serilog.Enrichers.Application.csproj | 16 + .../AbpSerilogEnrichersApplicationModule.cs | 9 + .../Application/AbpSerilogEnrichersConsts.cs | 8 + .../Application/ApplicationNameEnricher.cs | 30 ++ ...pplicationLoggerConfigurationExtensions.cs | 16 + .../FodyWeavers.xml | 3 + ...anhe.Abp.Serilog.Enrichers.UniqueId.csproj | 19 + .../AbpSerilogEnrichersUniqueIdModule.cs | 17 + .../AbpSerilogEnrichersUniqueIdOptions.cs | 12 + .../UniqueId/AbpSerilogUniqueIdConsts.cs | 7 + .../Enrichers/UniqueId/UniqueIdEnricher.cs | 19 + .../UniqueIdLoggerConfigurationExtensions.cs | 16 + 119 files changed, 5124 insertions(+) create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/README.md create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe.Abp.AuditLogging.Elasticsearch.csproj create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchModule.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchOptions.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AuditLogInfoToAuditLogConverter.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchAuditLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchEntityChangeStore.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchSecurityLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IAuditLogInfoToAuditLogConverter.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexInitializer.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexNameNormalizer.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializerService.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexNameNormalizer.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/README.md create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe.Abp.AuditLogging.EntityFrameworkCore.csproj create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreModule.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditingMapperProfile.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AuditLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/SecurityLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe.Abp.AuditLogging.csproj create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AbpAuditLoggingModule.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLog.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLogAction.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditingStore.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultAuditLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultEntityChangeStore.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultSecurityLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChange.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChangeWithUsername.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityPropertyChange.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IAuditLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IEntityChangeStore.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/ISecurityLogManager.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLog.cs create mode 100644 modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLogStore.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe.Abp.Auditing.Application.Contracts.csproj create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AbpAuditingApplicationContractsModule.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogActionDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogGetByPagedDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetByPagedDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetWithUsernameDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeWithUsernameDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityPropertyChangeDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IAuditLogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IEntityChangesAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditingRemoteServiceConsts.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureNames.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/en.json create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/zh-Hans.json create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogExceptionDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogFieldDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogGetByPagedDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/ILogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionDefinitionProvider.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionNames.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/ISecurityLogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogGetByPagedDto.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe.Abp.Auditing.Application.csproj create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingApplicationModule.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingMapperProfile.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/AuditLogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/EntityChangesAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditingApplicationServiceBase.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/Logging/LogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogAppService.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/FodyWeavers.xml create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe.Abp.Auditing.HttpApi.csproj create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AbpAuditingHttpApiModule.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/AuditLogController.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/EntityChangesController.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/Logging/LogController.cs create mode 100644 modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogController.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/FodyWeavers.xml create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/README.md create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe.Abp.Logging.Serilog.Elasticsearch.csproj create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchMapperProfile.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchModule.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchOptions.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogElasticsearchLoggingManager.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogException.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogField.cs create mode 100644 modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogInfo.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/FodyWeavers.xml create mode 100644 modules/logging/Sanhe.Abp.Logging/README.md create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe.Abp.Logging.csproj create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingEnricherPropertyNames.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingModule.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/DefaultLoggingManager.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/ILoggingManager.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogException.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogField.cs create mode 100644 modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogInfo.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/FodyWeavers.xml create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/README.md create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe.Abp.Serilog.Enrichers.Application.csproj create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersApplicationModule.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersConsts.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/ApplicationNameEnricher.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Serilog/ApplicationLoggerConfigurationExtensions.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/FodyWeavers.xml create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdModule.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdOptions.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogUniqueIdConsts.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/UniqueIdEnricher.cs create mode 100644 modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Serilog/UniqueIdLoggerConfigurationExtensions.cs diff --git a/Directory.Build.props b/Directory.Build.props index a6ba50d..01c9358 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,5 +5,7 @@ 6.0.* 1.9.7 7.15.1 + 2.10.0 + 8.4.1 \ No newline at end of file diff --git a/Sanhe.Abp.Framework.sln b/Sanhe.Abp.Framework.sln index eb6a0fd..38ff7bf 100644 --- a/Sanhe.Abp.Framework.sln +++ b/Sanhe.Abp.Framework.sln @@ -109,6 +109,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.MenuManagement.Ht EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "logging", "logging", "{56577207-AF97-4354-9C7A-ABBEE80FA1CA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "auditing", "auditing", "{5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.Logging", "modules\logging\Sanhe.Abp.Logging\Sanhe.Abp.Logging.csproj", "{1E0D237A-070E-44A5-A2E5-B1EA3FF397C3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Serilog.Enrichers.UniqueId", "modules\logging\Sanhe.Abp.Serilog.Enrichers.UniqueId\Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj", "{68056A12-29B7-41F5-9ABC-1E3BFFC5BF02}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Serilog.Enrichers.Application", "modules\logging\Sanhe.Abp.Serilog.Enrichers.Application\Sanhe.Abp.Serilog.Enrichers.Application.csproj", "{3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Logging.Serilog.Elasticsearch", "modules\logging\Sanhe.Abp.Logging.Serilog.Elasticsearch\Sanhe.Abp.Logging.Serilog.Elasticsearch.csproj", "{0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.AuditLogging", "modules\auditing\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj", "{11D7834B-EB00-4940-AB94-1835AACA0F13}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.AuditLogging.Elasticsearch", "modules\auditing\Sanhe.Abp.AuditLogging.Elasticsearch\Sanhe.Abp.AuditLogging.Elasticsearch.csproj", "{539069FD-3973-4DEB-A1EC-2BD25753698C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.AuditLogging.EntityFrameworkCore", "modules\auditing\Sanhe.Abp.AuditLogging.EntityFrameworkCore\Sanhe.Abp.AuditLogging.EntityFrameworkCore.csproj", "{8B73A116-D859-439A-B3F0-BB8DE902D2D2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Auditing.Application.Contracts", "modules\auditing\Sanhe.Abp.Auditing.Application.Contracts\Sanhe.Abp.Auditing.Application.Contracts.csproj", "{D014ED3D-E73A-4AFB-873E-C50B81493C8B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Auditing.Application", "modules\auditing\Sanhe.Abp.Auditing.Application\Sanhe.Abp.Auditing.Application.csproj", "{58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Auditing.HttpApi", "modules\auditing\Sanhe.Abp.Auditing.HttpApi\Sanhe.Abp.Auditing.HttpApi.csproj", "{A82C2175-AD0D-4AF5-B4F9-E264E47617C3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -267,6 +289,46 @@ Global {4A545053-9CD6-4578-92AF-1637AAC79F80}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A545053-9CD6-4578-92AF-1637AAC79F80}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A545053-9CD6-4578-92AF-1637AAC79F80}.Release|Any CPU.Build.0 = Release|Any CPU + {1E0D237A-070E-44A5-A2E5-B1EA3FF397C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E0D237A-070E-44A5-A2E5-B1EA3FF397C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E0D237A-070E-44A5-A2E5-B1EA3FF397C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E0D237A-070E-44A5-A2E5-B1EA3FF397C3}.Release|Any CPU.Build.0 = Release|Any CPU + {68056A12-29B7-41F5-9ABC-1E3BFFC5BF02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68056A12-29B7-41F5-9ABC-1E3BFFC5BF02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68056A12-29B7-41F5-9ABC-1E3BFFC5BF02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68056A12-29B7-41F5-9ABC-1E3BFFC5BF02}.Release|Any CPU.Build.0 = Release|Any CPU + {3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6}.Release|Any CPU.Build.0 = Release|Any CPU + {0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E}.Release|Any CPU.Build.0 = Release|Any CPU + {11D7834B-EB00-4940-AB94-1835AACA0F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11D7834B-EB00-4940-AB94-1835AACA0F13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11D7834B-EB00-4940-AB94-1835AACA0F13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11D7834B-EB00-4940-AB94-1835AACA0F13}.Release|Any CPU.Build.0 = Release|Any CPU + {539069FD-3973-4DEB-A1EC-2BD25753698C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {539069FD-3973-4DEB-A1EC-2BD25753698C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {539069FD-3973-4DEB-A1EC-2BD25753698C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {539069FD-3973-4DEB-A1EC-2BD25753698C}.Release|Any CPU.Build.0 = Release|Any CPU + {8B73A116-D859-439A-B3F0-BB8DE902D2D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B73A116-D859-439A-B3F0-BB8DE902D2D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B73A116-D859-439A-B3F0-BB8DE902D2D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B73A116-D859-439A-B3F0-BB8DE902D2D2}.Release|Any CPU.Build.0 = Release|Any CPU + {D014ED3D-E73A-4AFB-873E-C50B81493C8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D014ED3D-E73A-4AFB-873E-C50B81493C8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D014ED3D-E73A-4AFB-873E-C50B81493C8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D014ED3D-E73A-4AFB-873E-C50B81493C8B}.Release|Any CPU.Build.0 = Release|Any CPU + {58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7}.Release|Any CPU.Build.0 = Release|Any CPU + {A82C2175-AD0D-4AF5-B4F9-E264E47617C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A82C2175-AD0D-4AF5-B4F9-E264E47617C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A82C2175-AD0D-4AF5-B4F9-E264E47617C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A82C2175-AD0D-4AF5-B4F9-E264E47617C3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -319,6 +381,17 @@ Global {F60BA528-E8FE-465D-BAED-EAC48DE57847} = {1D9012F7-AAA3-49E0-9852-4A97FFB26E8C} {4A545053-9CD6-4578-92AF-1637AAC79F80} = {1D9012F7-AAA3-49E0-9852-4A97FFB26E8C} {56577207-AF97-4354-9C7A-ABBEE80FA1CA} = {F5F5D604-531B-4B57-A88E-C9C5CEEC55D7} + {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} = {F5F5D604-531B-4B57-A88E-C9C5CEEC55D7} + {1E0D237A-070E-44A5-A2E5-B1EA3FF397C3} = {56577207-AF97-4354-9C7A-ABBEE80FA1CA} + {68056A12-29B7-41F5-9ABC-1E3BFFC5BF02} = {56577207-AF97-4354-9C7A-ABBEE80FA1CA} + {3F2C7B9A-4145-42BF-9470-D7FCAF5D25A6} = {56577207-AF97-4354-9C7A-ABBEE80FA1CA} + {0DE6DCC3-48CA-4F2F-B6B1-88AA37B0208E} = {56577207-AF97-4354-9C7A-ABBEE80FA1CA} + {11D7834B-EB00-4940-AB94-1835AACA0F13} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} + {539069FD-3973-4DEB-A1EC-2BD25753698C} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} + {8B73A116-D859-439A-B3F0-BB8DE902D2D2} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} + {D014ED3D-E73A-4AFB-873E-C50B81493C8B} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} + {58BE1EAE-9C41-4ED4-B4B9-4F8052B3E9D7} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} + {A82C2175-AD0D-4AF5-B4F9-E264E47617C3} = {5BD41DAA-FBB1-4F52-A5D5-3C1D6A123645} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/README.md b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/README.md new file mode 100644 index 0000000..3da5f31 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/README.md @@ -0,0 +1,38 @@ +# LINGYUN.Abp.AuditLogging.Elasticsearch + +审计模块 Elasticsearch 实现 + +ElasticsearchAuditLogManager 实现了 IAuditLogManager, 审计日志由ES管理 +ElasticsearchSecurityLogManager 实现了 ISecurityLogManager, 安全日志由ES管理 + +## 模块引用 + + +```csharp +[DependsOn(typeof(AbpAuditLoggingElasticsearchModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` + +## 配置项 + +* AbpAuditLoggingElasticsearchOptions.IndexPrefix 索引前缀, 默认 auditlogging + +## 注意事项 + +与租户模块集成, 跨租户时将会切换索引 + +## appsettings.json + +```json +{ + "AuditLogging": { + "Elasticsearch": { + "IndexPrefix": "auditlogging" + } + } +} + +``` \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe.Abp.AuditLogging.Elasticsearch.csproj b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe.Abp.AuditLogging.Elasticsearch.csproj new file mode 100644 index 0000000..e895200 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe.Abp.AuditLogging.Elasticsearch.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchModule.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchModule.cs new file mode 100644 index 0000000..eab799d --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchModule.cs @@ -0,0 +1,22 @@ +using Sanhe.Abp.Elasticsearch; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Json; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + [DependsOn( + typeof(AbpAuditLoggingModule), + typeof(AbpElasticsearchModule), + typeof(AbpJsonModule))] + public class AbpAuditLoggingElasticsearchModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + Configure(configuration.GetSection("AuditLogging:Elasticsearch")); + + context.Services.AddHostedService(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchOptions.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchOptions.cs new file mode 100644 index 0000000..f528bd3 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AbpAuditLoggingElasticsearchOptions.cs @@ -0,0 +1,17 @@ +using Nest; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public class AbpAuditLoggingElasticsearchOptions + { + public const string DefaultIndexPrefix = "auditlogging"; + public string IndexPrefix { get; set; } + public IIndexSettings IndexSettings { get; set; } + + public AbpAuditLoggingElasticsearchOptions() + { + IndexPrefix = DefaultIndexPrefix; + IndexSettings = new IndexSettings(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AuditLogInfoToAuditLogConverter.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AuditLogInfoToAuditLogConverter.cs new file mode 100644 index 0000000..c01dcdc --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/AuditLogInfoToAuditLogConverter.cs @@ -0,0 +1,104 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +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; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency + { + protected IGuidGenerator GuidGenerator { get; } + protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; } + protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } + protected IJsonSerializer JsonSerializer { get; } + + public AuditLogInfoToAuditLogConverter( + IGuidGenerator guidGenerator, + IOptions exceptionHandlingOptions, + IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, + IJsonSerializer jsonSerializer) + { + GuidGenerator = guidGenerator; + ExceptionHandlingOptions = exceptionHandlingOptions.Value; + ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; + JsonSerializer = jsonSerializer; + } + + public virtual Task 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 EntityChange(GuidGenerator, auditLogId, entityChangeInfo, tenantId: auditLogInfo.TenantId)) + .ToList() + ?? new List(); + + var actions = auditLogInfo + .Actions? + .Select(auditLogActionInfo => new AuditLogAction(GuidGenerator.Create(), auditLogId, auditLogActionInfo, tenantId: auditLogInfo.TenantId)) + .ToList() + ?? new List(); + + var remoteServiceErrorInfos = auditLogInfo.Exceptions?.Select(exception => + ExceptionToErrorInfoConverter.Convert(exception, options => + { + options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients; + options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients; + })) ?? new List(); + + var exceptions = remoteServiceErrorInfos.Any() + ? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true) + : null; + + var comments = auditLogInfo + .Comments? + .JoinAsString(Environment.NewLine); + + var auditLog = new AuditLog( + 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.ImpersonatorTenantId, + extraProperties, + entityChanges, + actions, + exceptions, + comments + ); + + return Task.FromResult(auditLog); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchAuditLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchAuditLogManager.cs new file mode 100644 index 0000000..8701f07 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchAuditLogManager.cs @@ -0,0 +1,362 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Nest; +using Sanhe.Abp.Elasticsearch; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + [Dependency(ReplaceServices = true)] + public class ElasticsearchAuditLogManager : IAuditLogManager, ITransientDependency + { + private readonly AbpAuditingOptions _auditingOptions; + private readonly AbpElasticsearchOptions _elasticsearchOptions; + private readonly IIndexNameNormalizer _indexNameNormalizer; + private readonly IElasticsearchClientFactory _clientFactory; + private readonly IAuditLogInfoToAuditLogConverter _converter; + + public ILogger Logger { protected get; set; } + + public ElasticsearchAuditLogManager( + IIndexNameNormalizer indexNameNormalizer, + IOptions elasticsearchOptions, + IElasticsearchClientFactory clientFactory, + IOptions auditingOptions, + IAuditLogInfoToAuditLogConverter converter) + { + _converter = converter; + _clientFactory = clientFactory; + _auditingOptions = auditingOptions.Value; + _elasticsearchOptions = elasticsearchOptions.Value; + _indexNameNormalizer = indexNameNormalizer; + + Logger = NullLogger.Instance; + } + + + public virtual async Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string httpMethod = null, + string url = null, + Guid? userId = null, + string userName = null, + string applicationName = null, + string correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + httpMethod, + url, + userId, + userName, + applicationName, + correlationId, + clientId, + clientIpAddress, + maxExecutionDuration, + minExecutionDuration, + hasException, + httpStatusCode); + + var response = await client.CountAsync(dsl => + dsl.Index(CreateIndex()) + .Query(log => log.Bool(b => b.Must(querys.ToArray()))), + cancellationToken); + + return response.Count; + } + + public virtual async Task> 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 correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) + ? SortOrder.Ascending : SortOrder.Descending; + sorting = !sorting.IsNullOrWhiteSpace() + ? sorting.Split()[0] + : nameof(AuditLog.ExecutionTime); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + httpMethod, + url, + userId, + userName, + applicationName, + correlationId, + clientId, + clientIpAddress, + maxExecutionDuration, + minExecutionDuration, + hasException, + httpStatusCode); + + SourceFilterDescriptor SourceFilter(SourceFilterDescriptor selector) + { + selector.IncludeAll(); + if (!includeDetails) + { + selector.Excludes(field => + field.Field(f => f.Actions) + .Field(f => f.Comments) + .Field(f => f.Exceptions) + .Field(f => f.EntityChanges)); + } + + return selector; + } + + var response = await client.SearchAsync(dsl => + dsl.Index(CreateIndex()) + .Query(log => log.Bool(b => b.Must(querys.ToArray()))) + .Source(SourceFilter) + .Sort(log => log.Field(GetField(sorting), sortOrder)) + .From(skipCount) + .Size(maxResultCount), + cancellationToken); + + return response.Documents.ToList(); + } + + public virtual async Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var response = await client.GetAsync( + id, + dsl => + dsl.Index(CreateIndex()), + cancellationToken); + + return response.Source; + } + + public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + await client.DeleteAsync( + id, + dsl => + dsl.Index(CreateIndex()), + cancellationToken); + } + + public virtual async Task SaveAsync( + AuditLogInfo auditInfo, + CancellationToken cancellationToken = default) + { + if (!_auditingOptions.HideErrors) + { + return await SaveLogAsync(auditInfo, cancellationToken); + } + + try + { + return await SaveLogAsync(auditInfo, cancellationToken); + } + catch (Exception ex) + { + Logger.LogWarning("Could not save the audit log object: " + Environment.NewLine + auditInfo.ToString()); + Logger.LogException(ex, Microsoft.Extensions.Logging.LogLevel.Error); + } + return ""; + } + + protected virtual async Task SaveLogAsync( + AuditLogInfo auditLogInfo, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var auditLog = await _converter.ConvertAsync(auditLogInfo); + + var response = await client.IndexAsync( + auditLog, + (x) => x.Index(CreateIndex()) + .Id(auditLog.Id), + cancellationToken); + + return response.Id; + } + + protected virtual List, QueryContainer>> BuildQueryDescriptor( + DateTime? startTime = null, + DateTime? endTime = null, + string httpMethod = null, + string url = null, + Guid? userId = null, + string userName = null, + string applicationName = null, + string correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null) + { + var querys = new List, QueryContainer>>(); + + if (startTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(AuditLog.ExecutionTime))).GreaterThanOrEquals(startTime))); + } + if (endTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(AuditLog.ExecutionTime))).LessThanOrEquals(endTime))); + } + if (!httpMethod.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.HttpMethod))).Value(httpMethod))); + } + if (!url.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Wildcard((q) => q.Field(GetField(nameof(AuditLog.Url))).Value($"*{url}*"))); + } + if (userId.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.UserId))).Value(userId))); + } + if (!userName.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.UserName))).Value(userName))); + } + if (!applicationName.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ApplicationName))).Value(applicationName))); + } + if (!correlationId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.CorrelationId))).Value(correlationId))); + } + if (!clientId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ClientId))).Value(clientId))); + } + if (!clientIpAddress.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ClientIpAddress))).Value(clientIpAddress))); + } + if (maxExecutionDuration.HasValue) + { + querys.Add((log) => log.Range((q) => q.Field(GetField(nameof(AuditLog.ExecutionDuration))).LessThanOrEquals(maxExecutionDuration))); + } + if (minExecutionDuration.HasValue) + { + querys.Add((log) => log.Range((q) => q.Field(GetField(nameof(AuditLog.ExecutionDuration))).GreaterThanOrEquals(minExecutionDuration))); + } + + + if (hasException.HasValue) + { + if (hasException.Value) + { + querys.Add( + (q) => q.Bool( + (b) => b.Must( + (m) => m.Exists( + (e) => e.Field((f) => f.Exceptions))) + ) + ); + } + else + { + querys.Add( + (q) => q.Bool( + (b) => b.MustNot( + (mn) => mn.Exists( + (e) => e.Field( + (f) => f.Exceptions))) + ) + ); + } + } + + if (httpStatusCode.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.HttpStatusCode))).Value(httpStatusCode))); + } + + return querys; + } + + protected virtual string CreateIndex() + { + return _indexNameNormalizer.NormalizeIndex("audit-log"); + } + + private readonly static IDictionary _fieldMaps = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + { "Id", "Id.keyword" }, + { "ApplicationName", "ApplicationName.keyword" }, + { "UserId", "UserId.keyword" }, + { "UserName", "UserName.keyword" }, + { "TenantId", "TenantId.keyword" }, + { "TenantName", "TenantName.keyword" }, + { "ImpersonatorUserId", "ImpersonatorUserId.keyword" }, + { "ImpersonatorTenantId", "ImpersonatorTenantId.keyword" }, + { "ClientName", "ClientName.keyword" }, + { "ClientIpAddress", "ClientIpAddress.keyword" }, + { "ClientId", "ClientId.keyword" }, + { "CorrelationId", "CorrelationId.keyword" }, + { "BrowserInfo", "BrowserInfo.keyword" }, + { "HttpMethod", "HttpMethod.keyword" }, + { "Url", "Url.keyword" }, + { "ExecutionDuration", "ExecutionDuration" }, + { "ExecutionTime", "ExecutionTime" }, + { "HttpStatusCode", "HttpStatusCode" }, + }; + protected virtual string GetField(string field) + { + if (_fieldMaps.TryGetValue(field, out var mapField)) + { + return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); + } + + return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchEntityChangeStore.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchEntityChangeStore.cs new file mode 100644 index 0000000..97828c0 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchEntityChangeStore.cs @@ -0,0 +1,371 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Nest; +using Sanhe.Abp.Elasticsearch; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch; + +[Dependency(ReplaceServices = true)] +public class ElasticsearchEntityChangeStore : IEntityChangeStore, ITransientDependency +{ + private readonly AbpElasticsearchOptions _elasticsearchOptions; + private readonly IIndexNameNormalizer _indexNameNormalizer; + private readonly IElasticsearchClientFactory _clientFactory; + + public ILogger Logger { protected get; set; } + + public ElasticsearchEntityChangeStore( + IIndexNameNormalizer indexNameNormalizer, + IElasticsearchClientFactory clientFactory, + IOptions elasticsearchOptions) + { + _clientFactory = clientFactory; + _indexNameNormalizer = indexNameNormalizer; + _elasticsearchOptions = elasticsearchOptions.Value; + + Logger = NullLogger.Instance; + } + + public async virtual Task GetAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var resposne = await client.SearchAsync( + dsl => dsl.Index(CreateIndex()) + .Query(query => + query.Bool(bo => + bo.Must(m => + m.Nested(n => + n.InnerHits() + .Path("EntityChanges") + .Query(nq => + nq.Term(nqt => + nqt.Field(GetField(nameof(EntityChange.Id))).Value(entityChangeId))))))) + .Source(x => x.Excludes(f => f.Field("*"))) + .Sort(entity => entity.Field("EntityChanges.ChangeTime", SortOrder.Descending)) + .Size(1), + ct: cancellationToken); + + if (resposne.Shards.Successful > 0) + { + var hits = resposne.Hits.FirstOrDefault(); + if (hits.InnerHits.Count > 0) + { + return hits.InnerHits.First().Value.Documents().FirstOrDefault(); + } + } + + return null; + } + + public async virtual Task GetCountAsync( + Guid? auditLogId = null, + DateTime? startTime = null, + DateTime? endTime = null, + EntityChangeType? changeType = null, + string entityId = null, + string entityTypeFullName = null, + CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + return 0; + //var client = _clientFactory.Create(); + + //var querys = BuildQueryDescriptor( + // auditLogId, + // startTime, + // endTime, + // changeType, + // entityId, + // entityTypeFullName); + + //Func, QueryContainer> selector = q => q.MatchAll(); + //if (querys.Count > 0) + //{ + // selector = q => q.Bool(b => b.Must(querys.ToArray())); + //} + + //var response = await client.CountAsync(dsl => + // dsl.Index(CreateIndex()) + // .Query(q => + // q.Bool(b => + // b.Must(m => + // m.Nested(n => + // n.InnerHits(hit => hit.Source(s => s.ExcludeAll())) + // .Path("EntityChanges") + // .Query(selector) + // ) + // ) + // ) + // ), + // ct: cancellationToken); + + //return response.Count; + } + + public async virtual Task> GetListAsync( + 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) + { + // TODO: 需要解决Nested格式数据返回方式 + + //var client = _clientFactory.Create(); + + //var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) + // ? SortOrder.Ascending : SortOrder.Descending; + //sorting = !sorting.IsNullOrWhiteSpace() + // ? sorting.Split()[0] + // : nameof(EntityChange.ChangeTime); + + //var querys = BuildQueryDescriptor( + // auditLogId, + // startTime, + // endTime, + // changeType, + // entityId, + // entityTypeFullName); + + //SourceFilterDescriptor SourceFilter(SourceFilterDescriptor selector) + //{ + // selector.Includes(GetEntityChangeSources()); + // if (!includeDetails) + // { + // selector.Excludes(field => + // field.Field("EntityChanges.PropertyChanges") + // .Field("EntityChanges.ExtraProperties")); + // } + + // return selector; + //} + + //Func, QueryContainer> selector = q => q.MatchAll(); + //if (querys.Count > 0) + //{ + // selector = q => q.Bool(b => b.Must(querys.ToArray())); + //} + + //var response = await client.SearchAsync(dsl => + // dsl.Index(CreateIndex()) + // .Query(q => + // q.Bool(b => + // b.Must(m => + // m.Nested(n => + // n.InnerHits(hit => hit.Source(SourceFilter)) + // .Path("EntityChanges") + // .Query(selector) + // ) + // ) + // ) + // ) + // .Source(x => x.Excludes(f => f.Field("*"))) + // .Sort(entity => entity.Field(GetField(sorting), sortOrder)) + // .From(skipCount) + // .Size(maxResultCount), + // cancellationToken); + + //if (response.Shards.Successful > 0) + //{ + // var hits = response.Hits.FirstOrDefault(); + // if (hits.InnerHits.Count > 0) + // { + // return hits.InnerHits.First().Value.Documents().ToList(); + // } + //} + await Task.CompletedTask; + return new List(); + } + + public async virtual Task GetWithUsernameAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var response = await client.SearchAsync( + dsl => dsl.Index(CreateIndex()) + .Query(query => + query.Bool(bo => + bo.Must(m => + m.Nested(n => + n.InnerHits() + .Path("EntityChanges") + .Query(nq => + nq.Bool(nb => + nb.Must(nm => + nm.Term(nt => + nt.Field(GetField(nameof(EntityChange.Id))).Value(entityChangeId))))))))) + .Source(selector => selector.Includes(field => + field.Field(f => f.UserName))) + .Size(1), + ct: cancellationToken); + + var auditLog = response.Documents.FirstOrDefault(); + EntityChange entityChange = null; + + if (response.Shards.Successful > 0) + { + var hits = response.Hits.FirstOrDefault(); + if (hits.InnerHits.Count > 0) + { + entityChange = hits.InnerHits.First().Value.Documents().FirstOrDefault(); + } + } + + return new EntityChangeWithUsername() + { + EntityChange = entityChange, + UserName = auditLog?.UserName + }; + } + + public async virtual Task> GetWithUsernameAsync( + string entityId, + string entityTypeFullName, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var response = await client.SearchAsync( + dsl => dsl.Index(CreateIndex()) + .Query(query => + query.Bool(bo => + bo.Must(m => + m.Nested(n => + n.InnerHits() + .Path("EntityChanges") + .Query(nq => + nq.Bool(nb => + nb.Must(nm => + nm.Term(nt => + nt.Field(GetField(nameof(EntityChange.EntityId))).Value(entityId)), + nm => + nm.Term(nt => + nt.Field(GetField(nameof(EntityChange.EntityTypeFullName))).Value(entityTypeFullName)) + ) + ) + ) + ) + ) + ) + ) + .Source(selector => selector.Includes(field => + field.Field(f => f.UserName))) + .Sort(entity => entity.Field(f => f.ExecutionTime, SortOrder.Descending)), + ct: cancellationToken); + + if (response.Hits.Count > 0) + { + return response.Hits. + Select(hit => new EntityChangeWithUsername + { + UserName = hit.Source.UserName, + EntityChange = hit.InnerHits.Any() ? + hit.InnerHits.First().Value.Documents().FirstOrDefault() + : null + }) + .ToList(); + } + + return new List(); + } + + protected virtual List, QueryContainer>> BuildQueryDescriptor( + Guid? auditLogId = null, + DateTime? startTime = null, + DateTime? endTime = null, + EntityChangeType? changeType = null, + string entityId = null, + string entityTypeFullName = null) + { + var querys = new List, QueryContainer>>(); + + if (auditLogId.HasValue) + { + querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.AuditLogId))).Value(auditLogId))); + } + if (startTime.HasValue) + { + querys.Add(entity => entity.DateRange(q => q.Field(GetField(nameof(EntityChange.ChangeTime))).GreaterThanOrEquals(startTime))); + } + if (endTime.HasValue) + { + querys.Add(entity => entity.DateRange(q => q.Field(GetField(nameof(EntityChange.ChangeTime))).LessThanOrEquals(endTime))); + } + if (changeType.HasValue) + { + querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.ChangeType))).Value(changeType))); + } + if (!entityId.IsNullOrWhiteSpace()) + { + querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.EntityId))).Value(entityId))); + } + if (!entityTypeFullName.IsNullOrWhiteSpace()) + { + querys.Add(entity => entity.Wildcard(q => q.Field(GetField(nameof(EntityChange.EntityTypeFullName))).Value($"*{entityTypeFullName}*"))); + } + + return querys; + } + + protected virtual string CreateIndex() + { + return _indexNameNormalizer.NormalizeIndex("audit-log"); + } + + protected Func, IPromise> GetEntityChangeSources() + { + return field => field + .Field("EntityChanges.Id") + .Field("EntityChanges.AuditLogId") + .Field("EntityChanges.TenantId") + .Field("EntityChanges.ChangeTime") + .Field("EntityChanges.ChangeType") + .Field("EntityChanges.EntityTenantId") + .Field("EntityChanges.EntityId") + .Field("EntityChanges.EntityTypeFullName") + .Field("EntityChanges.PropertyChanges") + .Field("EntityChanges.ExtraProperties"); + } + + private readonly static IDictionary _fieldMaps = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + { "Id", "EntityChanges.Id.keyword" }, + { "AuditLogId", "EntityChanges.AuditLogId.keyword" }, + { "TenantId", "EntityChanges.TenantId.keyword" }, + { "EntityTenantId", "EntityChanges.EntityTenantId.keyword" }, + { "EntityId", "EntityChanges.EntityId.keyword" }, + { "EntityTypeFullName", "EntityChanges.EntityTypeFullName.keyword" }, + { "PropertyChanges", "EntityChanges.PropertyChanges" }, + { "ExtraProperties", "EntityChanges.ExtraProperties" }, + { "ChangeType", "EntityChanges.ChangeType" }, + { "ChangeTime", "EntityChanges.ChangeTime" }, + }; + protected virtual string GetField(string field) + { + if (_fieldMaps.TryGetValue(field, out var mapField)) + { + return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); + } + + return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchSecurityLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchSecurityLogManager.cs new file mode 100644 index 0000000..5caea63 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/ElasticsearchSecurityLogManager.cs @@ -0,0 +1,268 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Nest; +using Sanhe.Abp.Elasticsearch; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.SecurityLog; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + [Dependency(ReplaceServices = true)] + public class ElasticsearchSecurityLogManager : ISecurityLogManager, ITransientDependency + { + private readonly AbpSecurityLogOptions _securityLogOptions; + private readonly AbpElasticsearchOptions _elasticsearchOptions; + private readonly IIndexNameNormalizer _indexNameNormalizer; + private readonly IGuidGenerator _guidGenerator; + private readonly IElasticsearchClientFactory _clientFactory; + + public ILogger Logger { protected get; set; } + + public ElasticsearchSecurityLogManager( + IGuidGenerator guidGenerator, + IIndexNameNormalizer indexNameNormalizer, + IOptions securityLogOptions, + IOptions elasticsearchOptions, + IElasticsearchClientFactory clientFactory) + { + _guidGenerator = guidGenerator; + _clientFactory = clientFactory; + _indexNameNormalizer = indexNameNormalizer; + _securityLogOptions = securityLogOptions.Value; + _elasticsearchOptions = elasticsearchOptions.Value; + + Logger = NullLogger.Instance; + } + + public virtual async Task SaveAsync( + SecurityLogInfo securityLogInfo, + CancellationToken cancellationToken = default) + { + // TODO: 框架不把这玩意儿放在 ISecurityLogManager? + if (!_securityLogOptions.IsEnabled) + { + return; + } + + var client = _clientFactory.Create(); + + var securityLog = new SecurityLog( + _guidGenerator.Create(), + securityLogInfo); + + await client.IndexAsync( + securityLog, + (x) => x.Index(CreateIndex()) + .Id(securityLog.Id), + cancellationToken); + } + + public virtual async Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var response = await client.GetAsync( + id, + dsl => + dsl.Index(CreateIndex()), + cancellationToken); + + return response.Source; + } + + public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + await client.DeleteAsync( + id, + dsl => + dsl.Index(CreateIndex()), + cancellationToken); + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) + ? SortOrder.Ascending : SortOrder.Descending; + sorting = !sorting.IsNullOrWhiteSpace() + ? sorting.Split()[0] + : nameof(SecurityLog.CreationTime); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + applicationName, + identity, + action, + userId, + userName, + clientId, + clientIpAddress, + correlationId); + + var response = await client.SearchAsync(dsl => + dsl.Index(CreateIndex()) + .Query(log => log.Bool(b => b.Must(querys.ToArray()))) + .Source(log => log.IncludeAll()) + .Sort(log => log.Field(GetField(sorting), sortOrder)) + .From(skipCount) + .Size(maxResultCount), + cancellationToken); + + return response.Documents.ToList(); + } + + + public virtual async Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + applicationName, + identity, + action, + userId, + userName, + clientId, + clientIpAddress, + correlationId); + + var response = await client.CountAsync(dsl => + dsl.Index(CreateIndex()) + .Query(log => log.Bool(b => b.Must(querys.ToArray()))), + cancellationToken); + + return response.Count; + } + + protected virtual List, QueryContainer>> BuildQueryDescriptor( + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null) + { + var querys = new List, QueryContainer>>(); + + if (startTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SecurityLog.CreationTime))).GreaterThanOrEquals(startTime))); + } + if (endTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SecurityLog.CreationTime))).LessThanOrEquals(endTime))); + } + if (!applicationName.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ApplicationName))).Value(applicationName))); + } + if (!identity.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.Identity))).Value(identity))); + } + if (!action.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.Action))).Value(action))); + } + if (userId.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.UserId))).Value(userId))); + } + if (!userName.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.UserName))).Value(userName))); + } + if (!clientId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ClientId))).Value(clientId))); + } + if (!clientIpAddress.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ClientIpAddress))).Value(clientIpAddress))); + } + if (!correlationId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.CorrelationId))).Value(correlationId))); + } + + return querys; + } + + protected virtual string CreateIndex() + { + return _indexNameNormalizer.NormalizeIndex("security-log"); + } + + private readonly static IDictionary _fieldMaps = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + { "Id", "Id.keyword" }, + { "ApplicationName", "ApplicationName.keyword" }, + { "UserId", "UserId.keyword" }, + { "UserName", "UserName.keyword" }, + { "TenantId", "TenantId.keyword" }, + { "TenantName", "TenantName.keyword" }, + { "Identity", "Identity.keyword" }, + { "Action", "Action.keyword" }, + { "BrowserInfo", "BrowserInfo.keyword" }, + { "ClientIpAddress", "ClientIpAddress.keyword" }, + { "ClientId", "ClientId.keyword" }, + { "CorrelationId", "CorrelationId.keyword" }, + { "CreationTime", "CreationTime" }, + }; + protected virtual string GetField(string field) + { + if (_fieldMaps.TryGetValue(field, out var mapField)) + { + return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); + } + + return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IAuditLogInfoToAuditLogConverter.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IAuditLogInfoToAuditLogConverter.cs new file mode 100644 index 0000000..5e56c7b --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IAuditLogInfoToAuditLogConverter.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Auditing; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public interface IAuditLogInfoToAuditLogConverter + { + Task ConvertAsync(AuditLogInfo auditLogInfo); + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexInitializer.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexInitializer.cs new file mode 100644 index 0000000..6828c5e --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexInitializer.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public interface IIndexInitializer + { + Task InitializeAsync(); + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexNameNormalizer.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexNameNormalizer.cs new file mode 100644 index 0000000..d36169c --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IIndexNameNormalizer.cs @@ -0,0 +1,7 @@ +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public interface IIndexNameNormalizer + { + string NormalizeIndex(string index); + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs new file mode 100644 index 0000000..20c0633 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializer.cs @@ -0,0 +1,107 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Nest; +using Sanhe.Abp.Elasticsearch; +using System; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public class IndexInitializer : IIndexInitializer, ISingletonDependency + { + private readonly AbpJsonOptions _jsonOptions; + private readonly AbpAuditLoggingElasticsearchOptions _elasticsearchOptions; + private readonly IIndexNameNormalizer _nameNormalizer; + private readonly IElasticsearchClientFactory _clientFactory; + + public ILogger Logger { protected get; set; } + + public IndexInitializer( + IOptions jsonOptions, + IOptions elasticsearchOptions, + IIndexNameNormalizer nameNormalizer, + IElasticsearchClientFactory clientFactory) + { + _jsonOptions = jsonOptions.Value; + _elasticsearchOptions = elasticsearchOptions.Value; + _nameNormalizer = nameNormalizer; + _clientFactory = clientFactory; + + Logger = NullLogger.Instance; + } + + public virtual async Task InitializeAsync() + { + var client = _clientFactory.Create(); + var dateTimeFormat = !_jsonOptions.DefaultDateTimeFormat.IsNullOrWhiteSpace() + ? $"{_jsonOptions.DefaultDateTimeFormat}||strict_date_optional_time||epoch_millis" + : "strict_date_optional_time||epoch_millis"; + var indexState = new IndexState + { + Settings = _elasticsearchOptions.IndexSettings, + }; + await InitlizeAuditLogIndex(client, indexState, dateTimeFormat); + await InitlizeSecurityLogIndex(client, indexState, dateTimeFormat); + } + + protected virtual async Task InitlizeAuditLogIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat) + { + var indexName = _nameNormalizer.NormalizeIndex("audit-log"); + var indexExists = await client.Indices.ExistsAsync(indexName); + if (!indexExists.Exists) + { + var indexCreateResponse = await client.Indices.CreateAsync( + indexName, + dsl => dsl.InitializeUsing(indexState) + .Map(map => + map.AutoMap() + .Properties(mp => + mp.Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat)) + .Object(p => p.Name(n => n.ExtraProperties)) + .Nested(n => + n.AutoMap() + .Name(nameof(AuditLog.EntityChanges)) + .Properties(np => + np.Object(p => p.Name(n => n.ExtraProperties)) + .Date(p => p.Name(n => n.ChangeTime).Format(dateTimeFormat)) + .Nested(npn => npn.Name(nameof(EntityChange.PropertyChanges))))) + .Nested(n => n.Name(nameof(AuditLog.Actions)) + .AutoMap() + .Properties(np => + np.Object(p => p.Name(n => n.ExtraProperties)) + .Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat))))))); + if (!indexCreateResponse.IsValid) + { + Logger.LogWarning("Failed to initialize index and audit log may not be retrieved."); + Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); + } + } + } + + protected virtual async Task InitlizeSecurityLogIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat) + { + var indexName = _nameNormalizer.NormalizeIndex("security-log"); + var indexExists = await client.Indices.ExistsAsync(indexName); + if (!indexExists.Exists) + { + var indexCreateResponse = await client.Indices.CreateAsync( + indexName, + dsl => dsl.InitializeUsing(indexState) + .Map(map => + map.AutoMap() + .Properties(mp => + mp.Object(p => p.Name(n => n.ExtraProperties)) + .Date(p => p.Name(n => n.CreationTime).Format(dateTimeFormat))))); + if (!indexCreateResponse.IsValid) + { + Logger.LogWarning("Failed to initialize index and security log may not be retrieved."); + Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); + } + } + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializerService.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializerService.cs new file mode 100644 index 0000000..626823e --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexInitializerService.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Hosting; +using System.Threading; +using System.Threading.Tasks; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public class IndexInitializerService : BackgroundService + { + private readonly IIndexInitializer _indexInitializer; + + public IndexInitializerService(IIndexInitializer indexInitializer) + { + _indexInitializer = indexInitializer; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await _indexInitializer.InitializeAsync(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexNameNormalizer.cs b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexNameNormalizer.cs new file mode 100644 index 0000000..ab4eaaf --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.Elasticsearch/Sanhe/Abp/AuditLogging/Elasticsearch/IndexNameNormalizer.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Options; +using System; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Sanhe.Abp.AuditLogging.Elasticsearch +{ + public class IndexNameNormalizer : IIndexNameNormalizer, ISingletonDependency + { + private readonly ICurrentTenant _currentTenant; + private readonly AbpAuditLoggingElasticsearchOptions _options; + + public IndexNameNormalizer(ICurrentTenant currentTenant, IOptions options) + { + _currentTenant = currentTenant; + _options = options.Value; + } + + public string NormalizeIndex(string index) + { + if (_currentTenant.IsAvailable) + { + return $"{_options.IndexPrefix}-{index}-{_currentTenant.Id:N}"; + } + return _options.IndexPrefix.IsNullOrWhiteSpace() + ? index + : $"{_options.IndexPrefix}-{index}"; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/README.md b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/README.md new file mode 100644 index 0000000..8be6bfd --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/README.md @@ -0,0 +1,32 @@ +# LINGYUN.Abp.AuditLogging.EntityFrameworkCore + +审计模块 EntityFrameworkCore 实现, 此模块仅作为桥梁, 具体实现交给abp官方模块 + +AuditLogManager 实现了 IAuditLogManager, 审计日志由Volo.Abp.AuditLogging模块管理 +SecurityLogManager 实现了 ISecurityLogManager, 安全日志由Volo.Abp.Identity模块管理 + +## 模块引用 + +```csharp +[DependsOn(typeof(AbpAuditLoggingEntityFrameworkCoreModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` + +## 配置项 + +请遵循 Volo.Abp.AuditLogging、Volo.Abp.Identity模块中的配置 + +## appsettings.json + +```json +{ + "ConnectionStrings": { + "AbpIdentity": "Server=127.0.0.1;Database=Identity;User Id=root;Password=*", + "AbpAuditLogging": "Server=127.0.0.1;Database=AuditLogging;User Id=root;Password=*", + } +} + +``` \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe.Abp.AuditLogging.EntityFrameworkCore.csproj b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe.Abp.AuditLogging.EntityFrameworkCore.csproj new file mode 100644 index 0000000..27e041c --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe.Abp.AuditLogging.EntityFrameworkCore.csproj @@ -0,0 +1,21 @@ + + + + + + + net6.0 + + + + + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreModule.cs b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreModule.cs new file mode 100644 index 0000000..f411be6 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreModule.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore +{ + [DependsOn( + typeof(Volo.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule), + typeof(Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingEntityFrameworkCoreModule))] + [DependsOn( + typeof(AbpAuditLoggingModule), + typeof(AbpAutoMapperModule))] + public class AbpAuditLoggingEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditingMapperProfile.cs b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditingMapperProfile.cs new file mode 100644 index 0000000..c1fe363 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AbpAuditingMapperProfile.cs @@ -0,0 +1,21 @@ +using AutoMapper; + +namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore +{ + public class AbpAuditingMapperProfile : Profile + { + public AbpAuditingMapperProfile() + { + CreateMap() + .MapExtraProperties(); + CreateMap(); + CreateMap() + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + + CreateMap() + .MapExtraProperties(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AuditLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AuditLogManager.cs new file mode 100644 index 0000000..659993a --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/AuditLogManager.cs @@ -0,0 +1,179 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.AuditLogging; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ObjectMapping; +using Volo.Abp.Uow; + +namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore +{ + [Dependency(ReplaceServices = true)] + public class AuditLogManager : IAuditLogManager, ISingletonDependency + { + protected IObjectMapper ObjectMapper { get; } + protected IAuditLogRepository AuditLogRepository { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected AbpAuditingOptions Options { get; } + protected IAuditLogInfoToAuditLogConverter Converter { get; } + + public ILogger Logger { protected get; set; } + + public AuditLogManager( + IObjectMapper objectMapper, + IAuditLogRepository auditLogRepository, + IUnitOfWorkManager unitOfWorkManager, + IOptions options, + IAuditLogInfoToAuditLogConverter converter) + { + ObjectMapper = objectMapper; + AuditLogRepository = auditLogRepository; + UnitOfWorkManager = unitOfWorkManager; + Converter = converter; + Options = options.Value; + + Logger = NullLogger.Instance; + } + + + public virtual async Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string httpMethod = null, + string url = null, + Guid? userId = null, + string userName = null, + string applicationName = null, + string correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + CancellationToken cancellationToken = default) + { + return await AuditLogRepository.GetCountAsync( + startTime, + endTime, + httpMethod, + url, + userId, + userName, + applicationName, + clientIpAddress, + correlationId, + maxExecutionDuration, + minExecutionDuration, + hasException, + httpStatusCode, + cancellationToken); + } + + public virtual async Task> 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 correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var auditLogs = await AuditLogRepository.GetListAsync( + sorting, + maxResultCount, + skipCount, + startTime, + endTime, + httpMethod, + url, + userId, + userName, + applicationName, + clientIpAddress, + correlationId, + maxExecutionDuration, + minExecutionDuration, + hasException, + httpStatusCode, + includeDetails, + cancellationToken); + + return ObjectMapper.Map, List>(auditLogs); + } + + public virtual async Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var auditLog = await AuditLogRepository.GetAsync(id, includeDetails, cancellationToken); + + return ObjectMapper.Map(auditLog); + } + + public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + using (var uow = UnitOfWorkManager.Begin(true)) + { + await AuditLogRepository.DeleteAsync(id); + await uow.CompleteAsync(); + } + } + + public virtual async Task SaveAsync( + AuditLogInfo auditInfo, + CancellationToken cancellationToken = default) + { + if (!Options.HideErrors) + { + return await SaveLogAsync(auditInfo, cancellationToken); + } + + try + { + return await SaveLogAsync(auditInfo, cancellationToken); + } + catch (Exception ex) + { + Logger.LogWarning("Could not save the audit log object: " + Environment.NewLine + auditInfo.ToString()); + Logger.LogException(ex, LogLevel.Error); + } + return ""; + } + + protected virtual async Task SaveLogAsync( + AuditLogInfo auditInfo, + CancellationToken cancellationToken = default) + { + using (var uow = UnitOfWorkManager.Begin(true)) + { + var auditLog = await AuditLogRepository.InsertAsync( + await Converter.ConvertAsync(auditInfo), + false, + cancellationToken); + await uow.CompleteAsync(); + + return auditLog.Id.ToString(); + } + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/SecurityLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/SecurityLogManager.cs new file mode 100644 index 0000000..acb9fc9 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging.EntityFrameworkCore/Sanhe/Abp/AuditLogging/EntityFrameworkCore/SecurityLogManager.cs @@ -0,0 +1,144 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.Identity; +using Volo.Abp.ObjectMapping; +using Volo.Abp.SecurityLog; +using Volo.Abp.Uow; + +namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore +{ + [Dependency(ReplaceServices = true)] + public class SecurityLogManager : ISecurityLogManager, ISingletonDependency + { + public ILogger Logger { get; set; } + + protected IObjectMapper ObjectMapper { get; } + protected AbpSecurityLogOptions SecurityLogOptions { get; } + protected IIdentitySecurityLogRepository IdentitySecurityLogRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + + public SecurityLogManager( + IObjectMapper objectMapper, + ILogger logger, + IOptions securityLogOptions, + IIdentitySecurityLogRepository identitySecurityLogRepository, + IGuidGenerator guidGenerator, + IUnitOfWorkManager unitOfWorkManager) + { + Logger = logger; + ObjectMapper = objectMapper; + SecurityLogOptions = securityLogOptions.Value; + IdentitySecurityLogRepository = identitySecurityLogRepository; + GuidGenerator = guidGenerator; + UnitOfWorkManager = unitOfWorkManager; + } + + public virtual async Task SaveAsync( + SecurityLogInfo securityLogInfo, + CancellationToken cancellationToken = default) + { + if (!SecurityLogOptions.IsEnabled) + { + return; + } + + using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) + { + await IdentitySecurityLogRepository.InsertAsync( + new IdentitySecurityLog(GuidGenerator, securityLogInfo), + false, + cancellationToken); + await uow.CompleteAsync(); + } + } + + public virtual async Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var securityLog = await IdentitySecurityLogRepository.GetAsync(id, includeDetails, cancellationToken); + + return ObjectMapper.Map(securityLog); + } + + public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + using (var uow = UnitOfWorkManager.Begin(true)) + { + await IdentitySecurityLogRepository.DeleteAsync(id); + await uow.CompleteAsync(); + } + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var securityLogs = await IdentitySecurityLogRepository.GetListAsync( + sorting, + maxResultCount, + skipCount, + startTime, + endTime, + applicationName, + identity, + action, + userId, + userName, + clientId, + correlationId, + includeDetails, + cancellationToken); + + return ObjectMapper.Map, List>(securityLogs); + } + + + public virtual async Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + CancellationToken cancellationToken = default) + { + return await IdentitySecurityLogRepository.GetCountAsync( + startTime, + endTime, + applicationName, + identity, + action, + userId, + userName, + clientId, + correlationId, + cancellationToken); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.AuditLogging/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe.Abp.AuditLogging.csproj b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe.Abp.AuditLogging.csproj new file mode 100644 index 0000000..0568b5c --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe.Abp.AuditLogging.csproj @@ -0,0 +1,17 @@ + + + + + + + netstandard2.0 + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AbpAuditLoggingModule.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AbpAuditLoggingModule.cs new file mode 100644 index 0000000..e64abff --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AbpAuditLoggingModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Auditing; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.AuditLogging +{ + [DependsOn( + typeof(AbpAuditingModule), + typeof(AbpGuidsModule), + typeof(AbpExceptionHandlingModule))] + public class AbpAuditLoggingModule : AbpModule + { + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLog.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLog.cs new file mode 100644 index 0000000..778ae67 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLog.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Auditing; +using Volo.Abp.Data; + +namespace Sanhe.Abp.AuditLogging +{ + [DisableAuditing] + public class AuditLog : IHasExtraProperties + { + public Guid Id { get; set; } + + public string ApplicationName { get; set; } + + public Guid? UserId { get; set; } + + public string UserName { get; set; } + + public Guid? TenantId { get; set; } + + public string TenantName { get; set; } + + public Guid? ImpersonatorUserId { get; set; } + + public Guid? ImpersonatorTenantId { get; set; } + + public DateTime ExecutionTime { get; set; } + + public int ExecutionDuration { get; set; } + + public string ClientIpAddress { get; set; } + + public string ClientName { get; set; } + + public string ClientId { get; set; } + + public string CorrelationId { get; set; } + + public string BrowserInfo { get; set; } + + public string HttpMethod { get; set; } + + public string Url { get; set; } + + public string Exceptions { get; set; } + + public string Comments { get; set; } + + public int? HttpStatusCode { get; set; } + + public List EntityChanges { get; set; } + + public List Actions { get; set; } + + public ExtraPropertyDictionary ExtraProperties { get; set; } + + public AuditLog() + { + Actions = new List(); + EntityChanges = new List(); + ExtraProperties = new ExtraPropertyDictionary(); + } + + public AuditLog( + Guid id, + string applicationName, + Guid? tenantId, + string tenantName, + Guid? userId, + string userName, + DateTime executionTime, + int executionDuration, + string clientIpAddress, + string clientName, + string clientId, + string correlationId, + string browserInfo, + string httpMethod, + string url, + int? httpStatusCode, + Guid? impersonatorUserId, + Guid? impersonatorTenantId, + ExtraPropertyDictionary extraPropertyDictionary, + List entityChanges, + List actions, + string exceptions, + string comments) + { + Id = id; + ApplicationName = applicationName; + TenantId = tenantId; + TenantName = tenantName; + UserId = userId; + UserName = userName; + ExecutionTime = executionTime; + ExecutionDuration = executionDuration; + ClientIpAddress = clientIpAddress; + ClientName = clientName; + ClientId = clientId; + CorrelationId = correlationId; + BrowserInfo = browserInfo; + HttpMethod = httpMethod; + Url = url; + HttpStatusCode = httpStatusCode; + ImpersonatorUserId = impersonatorUserId; + ImpersonatorTenantId = impersonatorTenantId; + + ExtraProperties = extraPropertyDictionary; + EntityChanges = entityChanges; + Actions = actions; + Exceptions = exceptions; + Comments = comments; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLogAction.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLogAction.cs new file mode 100644 index 0000000..ea87620 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditLogAction.cs @@ -0,0 +1,47 @@ +using System; +using Volo.Abp.Auditing; +using Volo.Abp.Data; + +namespace Sanhe.Abp.AuditLogging +{ + [DisableAuditing] + public class AuditLogAction : IHasExtraProperties + { + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public Guid AuditLogId { get; set; } + + public string ServiceName { get; set; } + + public string MethodName { get; set; } + + public string Parameters { get; set; } + + public DateTime ExecutionTime { get; set; } + + public int ExecutionDuration { get; set; } + + public ExtraPropertyDictionary ExtraProperties { get; set; } + + public AuditLogAction() + { + ExtraProperties = new ExtraPropertyDictionary(); + } + + public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null) + { + + Id = id; + TenantId = tenantId; + AuditLogId = auditLogId; + ExecutionTime = actionInfo.ExecutionTime; + ExecutionDuration = actionInfo.ExecutionDuration; + ExtraProperties = new ExtraPropertyDictionary(actionInfo.ExtraProperties); + ServiceName = actionInfo.ServiceName; + MethodName = actionInfo.MethodName; + Parameters = actionInfo.Parameters.Length > 2000 ? "" : actionInfo.Parameters; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditingStore.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditingStore.cs new file mode 100644 index 0000000..f43b765 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/AuditingStore.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.AuditLogging +{ + [Dependency(ReplaceServices = true)] + public class AuditingStore : IAuditingStore, ITransientDependency + { + private readonly IAuditLogManager _manager; + + public AuditingStore( + IAuditLogManager manager) + { + _manager = manager; + } + + public virtual async Task SaveAsync(AuditLogInfo auditInfo) + { + await _manager.SaveAsync(auditInfo); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultAuditLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultAuditLogManager.cs new file mode 100644 index 0000000..dcead79 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultAuditLogManager.cs @@ -0,0 +1,96 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.AuditLogging +{ + [Dependency(TryRegister = true)] + public class DefaultAuditLogManager : IAuditLogManager, ISingletonDependency + { + public ILogger Logger { protected get; set; } + + public DefaultAuditLogManager() + { + Logger = NullLogger.Instance; + } + + public virtual Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string httpMethod = null, + string url = null, + Guid? userId = null, + string userName = null, + string applicationName = null, + string correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No audit log manager is available!"); + return Task.FromResult(0L); + } + + public virtual Task> 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 correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No audit log manager is available!"); + return Task.FromResult(new List()); + } + + public virtual Task SaveAsync( + AuditLogInfo auditInfo, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No audit log manager is available and is written to the local log by default"); + Logger.LogInformation(auditInfo.ToString()); + + return Task.FromResult(""); + } + + public virtual Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No audit log manager is available!"); + + AuditLog auditLog = null; + return Task.FromResult(auditLog); + } + + public virtual Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + Logger.LogDebug("No audit log manager is available!"); + return Task.CompletedTask; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultEntityChangeStore.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultEntityChangeStore.cs new file mode 100644 index 0000000..c4a1785 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultEntityChangeStore.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.AuditLogging +{ + [Dependency(TryRegister = true)] + public class DefaultEntityChangeStore : IEntityChangeStore, ISingletonDependency + { + public Task GetAsync(Guid entityChangeId, CancellationToken cancellationToken = default) + { + EntityChange entityChange = null; + return Task.FromResult(entityChange); + } + + public Task GetCountAsync(Guid? auditLogId = null, DateTime? startTime = null, DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, string entityTypeFullName = null, CancellationToken cancellationToken = default) + { + return Task.FromResult(0L); + } + + public Task> GetListAsync(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) + { + return Task.FromResult(new List()); + } + + public Task GetWithUsernameAsync(Guid entityChangeId, CancellationToken cancellationToken = default) + { + EntityChangeWithUsername entityChange = null; + return Task.FromResult(entityChange); + } + + public Task> GetWithUsernameAsync(string entityId, string entityTypeFullName, CancellationToken cancellationToken = default) + { + return Task.FromResult(new List()); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultSecurityLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultSecurityLogManager.cs new file mode 100644 index 0000000..cac7e57 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/DefaultSecurityLogManager.cs @@ -0,0 +1,87 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.SecurityLog; + +namespace Sanhe.Abp.AuditLogging +{ + [Dependency(TryRegister = true)] + public class DefaultSecurityLogManager : ISecurityLogManager, ISingletonDependency + { + public ILogger Logger { protected get; set; } + + public DefaultSecurityLogManager() + { + Logger = NullLogger.Instance; + } + + public Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No security log manager is available!"); + return Task.FromResult(0L); + } + + public Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No security log manager is available!"); + return Task.FromResult(new List()); + } + + public Task SaveAsync( + SecurityLogInfo securityLogInfo, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No security log manager is available and is written to the local log by default"); + Logger.LogInformation(securityLogInfo.ToString()); + + return Task.CompletedTask; + } + + public virtual Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No security log manager is available!"); + + SecurityLog securityLog = null; + return Task.FromResult(securityLog); + } + + public virtual Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) + { + Logger.LogDebug("No security log manager is available!"); + return Task.CompletedTask; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChange.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChange.cs new file mode 100644 index 0000000..01732c4 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChange.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.Auditing; +using Volo.Abp.Data; +using Volo.Abp.Guids; + +namespace Sanhe.Abp.AuditLogging +{ + [DisableAuditing] + public class EntityChange : IHasExtraProperties + { + public Guid Id { get; set; } + + public Guid AuditLogId { get; set; } + + public Guid? TenantId { get; set; } + + public DateTime ChangeTime { get; set; } + + public EntityChangeType ChangeType { get; set; } + + public Guid? EntityTenantId { get; set; } + + public string EntityId { get; set; } + + public string EntityTypeFullName { get; set; } + + public List PropertyChanges { get; set; } + + public ExtraPropertyDictionary ExtraProperties { get; set; } + + public EntityChange() + { + PropertyChanges = new List(); + ExtraProperties = new ExtraPropertyDictionary(); + } + + public EntityChange( + IGuidGenerator guidGenerator, + Guid auditLogId, + EntityChangeInfo entityChangeInfo, + Guid? tenantId = null) + { + Id = guidGenerator.Create(); + AuditLogId = auditLogId; + TenantId = tenantId; + ChangeTime = entityChangeInfo.ChangeTime; + ChangeType = entityChangeInfo.ChangeType; + EntityId = entityChangeInfo.EntityId; + EntityTypeFullName = entityChangeInfo.EntityTypeFullName; + + PropertyChanges = entityChangeInfo + .PropertyChanges? + .Select(p => new EntityPropertyChange(guidGenerator, Id, p, tenantId)) + .ToList() + ?? new List(); + + ExtraProperties = new ExtraPropertyDictionary(); + if (entityChangeInfo.ExtraProperties != null) + { + foreach (var pair in entityChangeInfo.ExtraProperties) + { + ExtraProperties.Add(pair.Key, pair.Value); + } + } + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChangeWithUsername.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChangeWithUsername.cs new file mode 100644 index 0000000..d0a80b9 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityChangeWithUsername.cs @@ -0,0 +1,9 @@ +namespace Sanhe.Abp.AuditLogging +{ + public class EntityChangeWithUsername + { + public EntityChange EntityChange { get; set; } + + public string UserName { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityPropertyChange.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityPropertyChange.cs new file mode 100644 index 0000000..97cf661 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/EntityPropertyChange.cs @@ -0,0 +1,43 @@ +using System; +using Volo.Abp.Auditing; +using Volo.Abp.Guids; + +namespace Sanhe.Abp.AuditLogging +{ + [DisableAuditing] + public class EntityPropertyChange + { + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public Guid EntityChangeId { get; set; } + + public string NewValue { get; set; } + + public string OriginalValue { get; set; } + + public string PropertyName { get; set; } + + public string PropertyTypeFullName { get; set; } + + public EntityPropertyChange() + { + } + + public EntityPropertyChange( + IGuidGenerator guidGenerator, + Guid entityChangeId, + EntityPropertyChangeInfo entityChangeInfo, + Guid? tenantId = null) + { + Id = guidGenerator.Create(); + TenantId = tenantId; + EntityChangeId = entityChangeId; + NewValue = entityChangeInfo.NewValue; + OriginalValue = entityChangeInfo.OriginalValue; + PropertyName = entityChangeInfo.PropertyName; + PropertyTypeFullName = entityChangeInfo.PropertyTypeFullName; + } + } +} \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IAuditLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IAuditLogManager.cs new file mode 100644 index 0000000..606361f --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IAuditLogManager.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; + +namespace Sanhe.Abp.AuditLogging +{ + public interface IAuditLogManager + { + Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + Task DeleteAsync( + Guid id, + CancellationToken cancellationToken = default); + + Task SaveAsync( + AuditLogInfo auditInfo, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string httpMethod = null, + string url = null, + Guid? userId = null, + string userName = null, + string applicationName = null, + string correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + CancellationToken cancellationToken = default); + + Task> 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 correlationId = null, + string clientId = null, + string clientIpAddress = null, + int? maxExecutionDuration = null, + int? minExecutionDuration = null, + bool? hasException = null, + HttpStatusCode? httpStatusCode = null, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IEntityChangeStore.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IEntityChangeStore.cs new file mode 100644 index 0000000..f2e45cd --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/IEntityChangeStore.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Auditing; + +namespace Sanhe.Abp.AuditLogging +{ + public interface IEntityChangeStore + { + Task GetAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + Guid? auditLogId = null, + DateTime? startTime = null, + DateTime? endTime = null, + EntityChangeType? changeType = null, + string entityId = null, + string entityTypeFullName = null, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + 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 GetWithUsernameAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default); + + Task> GetWithUsernameAsync( + string entityId, + string entityTypeFullName, + CancellationToken cancellationToken = default); + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/ISecurityLogManager.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/ISecurityLogManager.cs new file mode 100644 index 0000000..7bfa9ff --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/ISecurityLogManager.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.SecurityLog; + +namespace Sanhe.Abp.AuditLogging +{ + public interface ISecurityLogManager + { + Task GetAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + Task DeleteAsync( + Guid id, + CancellationToken cancellationToken = default); + + Task SaveAsync( + SecurityLogInfo securityLogInfo, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + + Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + string applicationName = null, + string identity = null, + string action = null, + Guid? userId = null, + string userName = null, + string clientId = null, + string clientIpAddress = null, + string correlationId = null, + CancellationToken cancellationToken = default); + + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLog.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLog.cs new file mode 100644 index 0000000..3925adf --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLog.cs @@ -0,0 +1,72 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.SecurityLog; + +namespace Sanhe.Abp.AuditLogging +{ + public class SecurityLog : IHasExtraProperties + { + public Guid Id { get; set; } + + public Guid? TenantId { get; set; } + + public string ApplicationName { get; set; } + + public string Identity { get; set; } + + public string Action { get; set; } + + public Guid? UserId { get; set; } + + public string UserName { get; set; } + + public string TenantName { get; set; } + + public string ClientId { get; set; } + + public string CorrelationId { get; set; } + + public string ClientIpAddress { get; set; } + + public string BrowserInfo { get; set; } + + public DateTime CreationTime { get; set; } + + public ExtraPropertyDictionary ExtraProperties { get; set; } + + public SecurityLog() + { + ExtraProperties = new ExtraPropertyDictionary(); + } + + public SecurityLog(Guid id, SecurityLogInfo securityLogInfo) + { + Id = id; + TenantId = securityLogInfo.TenantId; + TenantName = securityLogInfo.TenantName; + + ApplicationName = securityLogInfo.ApplicationName; + Identity = securityLogInfo.Identity; + Action = securityLogInfo.Action; + + UserId = securityLogInfo.UserId; + UserName = securityLogInfo.UserName; + + CreationTime = securityLogInfo.CreationTime; + + ClientIpAddress = securityLogInfo.ClientIpAddress; + ClientId = securityLogInfo.ClientId; + CorrelationId = securityLogInfo.CorrelationId; + BrowserInfo = securityLogInfo.BrowserInfo; + + ExtraProperties = new ExtraPropertyDictionary(); + if (securityLogInfo.ExtraProperties != null) + { + foreach (var pair in securityLogInfo.ExtraProperties) + { + ExtraProperties.Add(pair.Key, pair.Value); + } + } + } + } +} diff --git a/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLogStore.cs b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLogStore.cs new file mode 100644 index 0000000..6b17f0a --- /dev/null +++ b/modules/auditing/Sanhe.Abp.AuditLogging/Sanhe/Abp/AuditLogging/SecurityLogStore.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.SecurityLog; + +namespace Sanhe.Abp.AuditLogging +{ + [Dependency(ReplaceServices = true)] + public class SecurityLogStore : ISecurityLogStore, ITransientDependency + { + private readonly ISecurityLogManager _manager; + + public SecurityLogStore( + ISecurityLogManager manager) + { + _manager = manager; + } + + public virtual async Task SaveAsync(SecurityLogInfo securityLogInfo) + { + await _manager.SaveAsync(securityLogInfo); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe.Abp.Auditing.Application.Contracts.csproj b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe.Abp.Auditing.Application.Contracts.csproj new file mode 100644 index 0000000..571fd66 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe.Abp.Auditing.Application.Contracts.csproj @@ -0,0 +1,28 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AbpAuditingApplicationContractsModule.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AbpAuditingApplicationContractsModule.cs new file mode 100644 index 0000000..bac0ea1 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AbpAuditingApplicationContractsModule.cs @@ -0,0 +1,34 @@ +using Volo.Abp.Application; +using Volo.Abp.AuditLogging; +using Volo.Abp.AuditLogging.Localization; +using Volo.Abp.Authorization; +using Volo.Abp.Features; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Sanhe.Abp.Auditing +{ + [DependsOn( + typeof(AbpFeaturesModule), + typeof(AbpAuthorizationModule), + typeof(AbpAuditLoggingDomainSharedModule), + typeof(AbpDddApplicationContractsModule))] + public class AbpAuditingApplicationContractsModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Sanhe/Abp/Auditing/Localization/Resources"); + }); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogActionDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogActionDto.cs new file mode 100644 index 0000000..44799f4 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogActionDto.cs @@ -0,0 +1,18 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public class AuditLogActionDto : ExtensibleEntityDto + { + public string ServiceName { get; set; } + + public string MethodName { get; set; } + + public string Parameters { get; set; } + + public DateTime ExecutionTime { get; set; } + + public int ExecutionDuration { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogDto.cs new file mode 100644 index 0000000..34b0dfc --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogDto.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public class AuditLogDto : ExtensibleEntityDto + { + public string ApplicationName { get; set; } + + public Guid? UserId { get; set; } + + public string UserName { get; set; } + + public Guid? TenantId { get; set; } + + public string TenantName { get; set; } + + public Guid? ImpersonatorUserId { get; set; } + + public Guid? ImpersonatorTenantId { get; set; } + + public DateTime ExecutionTime { get; set; } + + public int ExecutionDuration { get; set; } + + public string ClientIpAddress { get; set; } + + public string ClientName { get; set; } + + public string ClientId { get; set; } + + public string CorrelationId { get; set; } + + public string BrowserInfo { get; set; } + + public string HttpMethod { get; set; } + + public string Url { get; set; } + + public string Exceptions { get; set; } + + public string Comments { get; set; } + + public int? HttpStatusCode { get; set; } + public List EntityChanges { get; set; } + public List Actions { get; set; } + + public AuditLogDto() + { + EntityChanges = new List(); + Actions = new List(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogGetByPagedDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogGetByPagedDto.cs new file mode 100644 index 0000000..491b4aa --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/AuditLogGetByPagedDto.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public class AuditLogGetByPagedDto : PagedAndSortedResultRequestDto + { + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public string HttpMethod { get; set; } + public string Url { get; set; } + public Guid? UserId { get; set; } + public string UserName { get; set; } + public string ApplicationName { get; set; } + public string CorrelationId { get; set; } + public string ClientId { get; set; } + public string ClientIpAddress { get; set; } + public int? MaxExecutionDuration { get; set; } + public int? MinExecutionDuration { get; set; } + public bool? HasException { get; set; } + public HttpStatusCode? HttpStatusCode { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeDto.cs new file mode 100644 index 0000000..793d889 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeDto.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Auditing; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public class EntityChangeDto : ExtensibleEntityDto + { + public DateTime ChangeTime { get; set; } + + public EntityChangeType ChangeType { get; set; } + + public Guid? EntityTenantId { get; set; } + + public string EntityId { get; set; } + + public string EntityTypeFullName { get; set; } + + public List PropertyChanges { get; set; } + + public EntityChangeDto() + { + PropertyChanges = new List(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetByPagedDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetByPagedDto.cs new file mode 100644 index 0000000..fe1ec08 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetByPagedDto.cs @@ -0,0 +1,15 @@ +using System; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Auditing; + +namespace Sanhe.Abp.Auditing.AuditLogs; + +public class EntityChangeGetByPagedDto : PagedAndSortedResultRequestDto +{ + public Guid? AuditLogId { get; set; } + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public EntityChangeType? ChangeType { get; set; } + public string EntityId { get; set; } + public string EntityTypeFullName { get; set; } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetWithUsernameDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetWithUsernameDto.cs new file mode 100644 index 0000000..9af2eb6 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeGetWithUsernameDto.cs @@ -0,0 +1,7 @@ +namespace Sanhe.Abp.Auditing.AuditLogs; + +public class EntityChangeGetWithUsernameDto +{ + public string EntityId { get; set; } + public string EntityTypeFullName { get; set; } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeWithUsernameDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeWithUsernameDto.cs new file mode 100644 index 0000000..b3ed608 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityChangeWithUsernameDto.cs @@ -0,0 +1,8 @@ +namespace Sanhe.Abp.Auditing.AuditLogs; + +public class EntityChangeWithUsernameDto +{ + public EntityChangeDto EntityChange { get; set; } + + public string UserName { get; set; } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityPropertyChangeDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityPropertyChangeDto.cs new file mode 100644 index 0000000..497597d --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/EntityPropertyChangeDto.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public class EntityPropertyChangeDto : EntityDto + { + public string NewValue { get; set; } + + public string OriginalValue { get; set; } + + public string PropertyName { get; set; } + + public string PropertyTypeFullName { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IAuditLogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IAuditLogAppService.cs new file mode 100644 index 0000000..792cfcf --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IAuditLogAppService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + public interface IAuditLogAppService : IApplicationService + { + Task> GetListAsync(AuditLogGetByPagedDto input); + + Task GetAsync(Guid id); + + Task DeleteAsync(Guid id); + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IEntityChangesAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IEntityChangesAppService.cs new file mode 100644 index 0000000..0ffd214 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditLogs/IEntityChangesAppService.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Sanhe.Abp.Auditing.AuditLogs; + +public interface IEntityChangesAppService : IApplicationService +{ + Task GetAsync(Guid id); + + Task GetWithUsernameAsync(Guid id); + + Task> GetListAsync(EntityChangeGetByPagedDto input); + + Task> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input); +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditingRemoteServiceConsts.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditingRemoteServiceConsts.cs new file mode 100644 index 0000000..8371b3d --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/AuditingRemoteServiceConsts.cs @@ -0,0 +1,7 @@ +namespace Sanhe.Abp.Auditing +{ + public static class AuditingRemoteServiceConsts + { + public const string RemoteServiceName = "AbpAuditing"; + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs new file mode 100644 index 0000000..562d084 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs @@ -0,0 +1,43 @@ +using Volo.Abp.AuditLogging.Localization; +using Volo.Abp.Features; +using Volo.Abp.Localization; +using Volo.Abp.Validation.StringValues; + +namespace Sanhe.Abp.Auditing.Features +{ + public class AuditingFeatureDefinitionProvider : FeatureDefinitionProvider + { + public override void Define(IFeatureDefinitionContext context) + { + var auditingGroup = context.AddGroup( + name: AuditingFeatureNames.GroupName, + displayName: L("Features:Auditing")); + + var loggingFeature = auditingGroup.AddFeature( + name: AuditingFeatureNames.Logging.Default, + displayName: L("Features:Auditing"), + description: L("Features:Auditing") + ); + + loggingFeature.CreateChild( + name: AuditingFeatureNames.Logging.AuditLog, + defaultValue: true.ToString(), + displayName: L("Features:DisplayName:AuditLog"), + description: L("Features:Description:AuditLog"), + valueType: new ToggleStringValueType(new BooleanValueValidator()) + ); + loggingFeature.CreateChild( + name: AuditingFeatureNames.Logging.SecurityLog, + defaultValue: true.ToString(), + displayName: L("Features:DisplayName:SecurityLog"), + description: L("Features:Description:SecurityLog"), + valueType: new ToggleStringValueType(new BooleanValueValidator()) + ); + } + + protected LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureNames.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureNames.cs new file mode 100644 index 0000000..314def1 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Features/AuditingFeatureNames.cs @@ -0,0 +1,15 @@ +namespace Sanhe.Abp.Auditing.Features +{ + public static class AuditingFeatureNames + { + public const string GroupName = "AbpAuditing"; + public class Logging + { + public const string Default = GroupName + ".Logging"; + + public const string AuditLog = Default + ".AuditLog"; + + public const string SecurityLog = Default + ".SecurityLog"; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/en.json b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/en.json new file mode 100644 index 0000000..6e15b1e --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/en.json @@ -0,0 +1,88 @@ +{ + "culture": "en", + "texts": { + "Permissions:Auditing": "Auditing", + "Permissions:AuditLog": "Audit log", + "Permissions:SecurityLog": "Security log", + "Permissions:DeleteLog": "Delete", + "Features:Auditing": "Auditing", + "Features:DisplayName:AuditLog": "Audit log", + "Features:Description:AuditLog": "Whether to enable audit logging", + "Features:DisplayName:SecurityLog": "Security log", + "Features:Description:SecurityLog": "Whether to enable security logging", + "SecurityLog": "Security log", + "AuditLog": "Audit log", + "Logging": "System Log", + "Application": "Application", + "ApplicationName": "Application name", + "TenantId": "Tenant id", + "TenantName": "Tenant name", + "ImpersonatorTenantId": "Impersonator tenant", + "UserInfo": "User info", + "UserId": "User id", + "UserName": "User name", + "ImpersonatorUserId": "Impersonator user", + "ClientId": "Client id", + "ClientName": "Client name", + "ClientIpAddress": "Ip address", + "BrowserInfo": "Browser", + "Operation": "Operation", + "HttpMethod": "Http method", + "RequestUrl": "Request url", + "HttpStatusCode": "StatusCode", + "HasException": "Has exception", + "ExecutionTime": "Execution time", + "MinExecutionDuration": "Min execution duration(ms)", + "MaxExecutionDuration": "Max execution duration(ms)", + "ExecutionDuration": "Execution duration(ms)", + "Identity": "Identity", + "ActionName": "Action", + "CreationTime": "Creation time", + "Additional": "Additional", + "CorrelationId": "Correlation", + "StartTime": "Start time", + "EndTime": "End time", + "SelectDateTime": "Select time", + "InvokeMethod": "Invoke method", + "ServiceName": "Service", + "MethodName": "Method", + "Parameters": "Parameters", + "EntitiesChanged": "Entities changed", + "ChangeType": "Change type", + "EntityTypeFullName": "Entity type", + "EntityId": "Entity id", + "PropertyChanges": "Property changes", + "PropertyName": "Property name", + "NewValue": "New value", + "OriginalValue": "Original value", + "PropertyTypeFullName": "Property type", + "Exception": "Exception", + "StackTrack": "Stack track", + "SecrchLog": "Secrch", + "Created": "Created", + "Updated": "Updated", + "Deleted": "Deleted", + "ShowLogDialog": "Show log", + "DeleteLog": "Delete log", + "MachineName": "Machine", + "Environment": "Environment", + "Context": "Context", + "RequestId": "Request Id", + "RequestPath": "Request Path", + "ProcessId": "Process Id", + "ThreadId": "Thread Id", + "ActionId": "Action Id", + "ConnectionId": "Connection Id", + "Depth": "Depth", + "Class": "Class", + "Message": "Message", + "Source": "Source", + "StackTrace": "Stack Trace", + "HResult": "HResult", + "HelpURL": "Help Url", + "TimeStamp": "Timestamp", + "Level": "Level", + "Fields": "Fields", + "Exceptions": "Exceptions" + } +} \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/zh-Hans.json b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/zh-Hans.json new file mode 100644 index 0000000..4f86c82 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Localization/Resources/zh-Hans.json @@ -0,0 +1,88 @@ +{ + "culture": "zh-Hans", + "texts": { + "Permissions:Auditing": "内部审计", + "Permissions:AuditLog": "审计日志", + "Permissions:SecurityLog": "安全日志", + "Permissions:DeleteLog": "删除日志", + "Features:Auditing": "内部审计", + "Features:DisplayName:AuditLog": "审计日志", + "Features:Description:AuditLog": "是否启用审计日志功能", + "Features:DisplayName:SecurityLog": "安全日志", + "Features:Description:SecurityLog": "是否启用安全日志功能", + "SecurityLog": "安全日志", + "AuditLog": "审计日志", + "Logging": "系统日志", + "Application": "应用信息", + "ApplicationName": "应用名称", + "TenantId": "租户标识", + "TenantName": "租户名称", + "ImpersonatorTenantId": "模拟租户", + "UserInfo": "用户信息", + "UserId": "用户标识", + "UserName": "用户名称", + "ImpersonatorUserId": "模拟用户", + "ClientId": "客户端标识", + "ClientName": "客户端名称", + "ClientIpAddress": "客户端地址", + "BrowserInfo": "浏览器信息", + "Operation": "操作信息", + "HttpMethod": "请求方法", + "RequestUrl": "请求路径", + "HttpStatusCode": "响应状态", + "HasException": "包含异常", + "ExecutionTime": "调用时间", + "MinExecutionDuration": "最短响应时间(ms)", + "MaxExecutionDuration": "最长响应时间(ms)", + "ExecutionDuration": "响应时间(ms)", + "Identity": "主体名称", + "ActionName": "方法名称", + "CreationTime": "创建时间", + "Additional": "附加信息", + "CorrelationId": "链路标识", + "StartTime": "开始时间", + "EndTime": "结束时间", + "SelectDateTime": "选择日期时间", + "InvokeMethod": "调用方法", + "ServiceName": "服务名称", + "MethodName": "方法名称", + "Parameters": "参数列表", + "EntitiesChanged": "实体变更", + "ChangeType": "变更类型", + "EntityTypeFullName": "实体类型", + "EntityId": "实体标识", + "PropertyChanges": "属性变更", + "PropertyName": "属性名称", + "NewValue": "当前值", + "OriginalValue": "原始值", + "PropertyTypeFullName": "属性类型", + "Exception": "异常信息", + "StackTrack": "异常堆栈", + "SecrchLog": "查询日志", + "Created": "新增", + "Updated": "修改", + "Deleted": "删除", + "ShowLogDialog": "查看日志", + "DeleteLog": "删除日志", + "MachineName": "机器名称", + "Environment": "应用环境", + "Context": "上下文", + "RequestId": "请求标识", + "RequestPath": "请求路径", + "ProcessId": "进程标识", + "ThreadId": "线程标识", + "ActionId": "方法标识", + "ConnectionId": "连接标识", + "Depth": "深度", + "Class": "类型", + "Message": "信息", + "Source": "来源", + "StackTrace": "堆栈", + "HResult": "代码", + "HelpURL": "帮助", + "TimeStamp": "时间戳", + "Level": "级别", + "Fields": "字段", + "Exceptions": "错误信息" + } +} \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogDto.cs new file mode 100644 index 0000000..4511194 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogDto.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; + +namespace Sanhe.Abp.Auditing.Logging.Dto +{ + public class LogDto + { + public DateTime TimeStamp { get; set; } + public LogLevel Level { get; set; } + public string Message { get; set; } + public LogFieldDto Fields { get; set; } + public List Exceptions { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogExceptionDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogExceptionDto.cs new file mode 100644 index 0000000..c4632c8 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogExceptionDto.cs @@ -0,0 +1,13 @@ +namespace Sanhe.Abp.Auditing.Logging.Dto +{ + public class LogExceptionDto + { + public int Depth { get; set; } + public string Class { get; set; } + public string Message { get; set; } + public string Source { get; set; } + public string StackTrace { get; set; } + public int HResult { get; set; } + public string HelpURL { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogFieldDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogFieldDto.cs new file mode 100644 index 0000000..f23d5e6 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogFieldDto.cs @@ -0,0 +1,21 @@ +namespace Sanhe.Abp.Auditing.Logging.Dto +{ + public class LogFieldDto + { + public string Id { get; set; } + public string MachineName { get; set; } + public string Environment { get; set; } + public string Application { get; set; } + public string Context { get; set; } + public string ActionId { get; set; } + public string ActionName { get; set; } + public string RequestId { get; set; } + public string RequestPath { get; set; } + public string ConnectionId { get; set; } + public string CorrelationId { get; set; } + public string ClientId { get; set; } + public string UserId { get; set; } + public int ProcessId { get; set; } + public int ThreadId { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogGetByPagedDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogGetByPagedDto.cs new file mode 100644 index 0000000..0589eb2 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/Dto/LogGetByPagedDto.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.Logging; +using System; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.Logging.Dto +{ + public class LogGetByPagedDto : PagedAndSortedResultRequestDto + { + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public LogLevel? Level { get; set; } + public string MachineName { get; set; } + public string Environment { get; set; } + public string Application { get; set; } + public string Context { get; set; } + public string RequestId { get; set; } + public string RequestPath { get; set; } + public string CorrelationId { get; set; } + public int? ProcessId { get; set; } + public int? ThreadId { get; set; } + public bool? HasException { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/ILogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/ILogAppService.cs new file mode 100644 index 0000000..56b429d --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Logging/ILogAppService.cs @@ -0,0 +1,14 @@ +using Sanhe.Abp.Auditing.Logging.Dto; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Sanhe.Abp.Auditing.Logging +{ + public interface ILogAppService : IApplicationService + { + Task GetAsync(string id); + + Task> GetListAsync(LogGetByPagedDto input); + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionDefinitionProvider.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionDefinitionProvider.cs new file mode 100644 index 0000000..f6d3e95 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionDefinitionProvider.cs @@ -0,0 +1,35 @@ +using Volo.Abp.AuditLogging.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace Sanhe.Abp.Auditing.Permissions +{ + public class AuditingPermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var auditingGroup = context.AddGroup( + name: AuditingPermissionNames.GroupName, + displayName: L("Permissions:Auditing")); + + var auditLogPermission = auditingGroup.AddPermission( + name: AuditingPermissionNames.AuditLog.Default, + displayName: L("Permissions:AuditLog")); + auditLogPermission.AddChild( + name: AuditingPermissionNames.AuditLog.Delete, + displayName: L("Permissions:DeleteLog")); + + var securityLogPermission = auditingGroup.AddPermission( + name: AuditingPermissionNames.SecurityLog.Default, + displayName: L("Permissions:SecurityLog")); + securityLogPermission.AddChild( + name: AuditingPermissionNames.SecurityLog.Delete, + displayName: L("Permissions:DeleteLog")); + } + + protected LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionNames.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionNames.cs new file mode 100644 index 0000000..0d04eaa --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/Permissions/AuditingPermissionNames.cs @@ -0,0 +1,18 @@ +namespace Sanhe.Abp.Auditing.Permissions +{ + public class AuditingPermissionNames + { + public const string GroupName = "AbpAuditing"; + public class AuditLog + { + public const string Default = GroupName + ".AuditLog"; + public const string Delete = Default + ".Delete"; + } + + public class SecurityLog + { + public const string Default = GroupName + ".SecurityLog"; + public const string Delete = Default + ".Delete"; + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/ISecurityLogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/ISecurityLogAppService.cs new file mode 100644 index 0000000..63ed3f9 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/ISecurityLogAppService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace Sanhe.Abp.Auditing.SecurityLogs +{ + public interface ISecurityLogAppService : IApplicationService + { + Task> GetListAsync(SecurityLogGetByPagedDto input); + + Task GetAsync(Guid id); + + Task DeleteAsync(Guid id); + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogDto.cs new file mode 100644 index 0000000..f4c4a13 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogDto.cs @@ -0,0 +1,30 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.SecurityLogs +{ + public class SecurityLogDto : ExtensibleEntityDto + { + public string ApplicationName { get; set; } + + public string Identity { get; set; } + + public string Action { get; set; } + + public Guid? UserId { get; set; } + + public string UserName { get; set; } + + public string TenantName { get; set; } + + public string ClientId { get; set; } + + public string CorrelationId { get; set; } + + public string ClientIpAddress { get; set; } + + public string BrowserInfo { get; set; } + + public DateTime CreationTime { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogGetByPagedDto.cs b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogGetByPagedDto.cs new file mode 100644 index 0000000..4919af7 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application.Contracts/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogGetByPagedDto.cs @@ -0,0 +1,18 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.SecurityLogs +{ + public class SecurityLogGetByPagedDto : PagedAndSortedResultRequestDto + { + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public string ApplicationName { get; set; } + public string Identity { get; set; } + public string ActionName { get; set; } + public Guid? UserId { get; set; } + public string UserName { get; set; } + public string ClientId { get; set; } + public string CorrelationId { get; set; } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.Auditing.Application/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe.Abp.Auditing.Application.csproj b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe.Abp.Auditing.Application.csproj new file mode 100644 index 0000000..44b627e --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe.Abp.Auditing.Application.csproj @@ -0,0 +1,22 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingApplicationModule.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingApplicationModule.cs new file mode 100644 index 0000000..599afac --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingApplicationModule.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Sanhe.Abp.AuditLogging; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.Auditing +{ + [DependsOn( + typeof(AbpAutoMapperModule), + typeof(AbpAuditLoggingModule), + typeof(AbpAuditingApplicationContractsModule))] + public class AbpAuditingApplicationModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingMapperProfile.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingMapperProfile.cs new file mode 100644 index 0000000..a5b8f59 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AbpAuditingMapperProfile.cs @@ -0,0 +1,31 @@ +using AutoMapper; +using Sanhe.Abp.Auditing.AuditLogs; +using Sanhe.Abp.Auditing.Logging.Dto; +using Sanhe.Abp.Auditing.SecurityLogs; +using Sanhe.Abp.AuditLogging; +using Sanhe.Abp.Logging; + +namespace Sanhe.Abp.Auditing +{ + public class AbpAuditingMapperProfile : Profile + { + public AbpAuditingMapperProfile() + { + CreateMap() + .MapExtraProperties(); + CreateMap(); + CreateMap(); + CreateMap() + .MapExtraProperties(); + CreateMap() + .MapExtraProperties(); + + CreateMap() + .MapExtraProperties(); + + CreateMap(); + CreateMap(); + CreateMap(); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/AuditLogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/AuditLogAppService.cs new file mode 100644 index 0000000..f3984fe --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/AuditLogAppService.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Authorization; +using Sanhe.Abp.Auditing.Features; +using Sanhe.Abp.Auditing.Permissions; +using Sanhe.Abp.AuditLogging; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Features; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + [Authorize(AuditingPermissionNames.AuditLog.Default)] + [RequiresFeature(AuditingFeatureNames.Logging.AuditLog)] + public class AuditLogAppService : AuditingApplicationServiceBase, IAuditLogAppService + { + protected IAuditLogManager AuditLogManager { get; } + + public AuditLogAppService(IAuditLogManager auditLogManager) + { + AuditLogManager = auditLogManager; + } + + public virtual async Task GetAsync(Guid id) + { + var auditLog = await AuditLogManager.GetAsync(id, includeDetails: true); + + return ObjectMapper.Map(auditLog); + } + + public virtual async Task> GetListAsync(AuditLogGetByPagedDto input) + { + var auditLogCount = await AuditLogManager + .GetCountAsync(input.StartTime, input.EndTime, + input.HttpMethod, input.Url, + input.UserId, input.UserName, + input.ApplicationName, input.CorrelationId, + input.ClientId, input.ClientIpAddress, + input.MaxExecutionDuration, input.MinExecutionDuration, + input.HasException, input.HttpStatusCode); + + var auditLogs = await AuditLogManager + .GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, + input.StartTime, input.EndTime, + input.HttpMethod, input.Url, + input.UserId, input.UserName, + input.ApplicationName, input.CorrelationId, + input.ClientId, input.ClientIpAddress, + input.MaxExecutionDuration, input.MinExecutionDuration, + input.HasException, input.HttpStatusCode, includeDetails: false); + + return new PagedResultDto(auditLogCount, + ObjectMapper.Map, List>(auditLogs)); + } + + [Authorize(AuditingPermissionNames.AuditLog.Delete)] + public virtual async Task DeleteAsync([Required] Guid id) + { + await AuditLogManager.DeleteAsync(id); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/EntityChangesAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/EntityChangesAppService.cs new file mode 100644 index 0000000..f9a2ac0 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditLogs/EntityChangesAppService.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Authorization; +using Sanhe.Abp.Auditing.Features; +using Sanhe.Abp.Auditing.Permissions; +using Sanhe.Abp.AuditLogging; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Features; + +namespace Sanhe.Abp.Auditing.AuditLogs; + +[Authorize(AuditingPermissionNames.AuditLog.Default)] +[RequiresFeature(AuditingFeatureNames.Logging.AuditLog)] +public class EntityChangesAppService : AuditingApplicationServiceBase, IEntityChangesAppService +{ + protected IEntityChangeStore EntityChangeStore { get; } + + public EntityChangesAppService(IEntityChangeStore entityChangeStore) + { + EntityChangeStore = entityChangeStore; + } + + public async virtual Task GetAsync(Guid id) + { + var entityChange = await EntityChangeStore.GetAsync(id); + + return ObjectMapper.Map(entityChange); + } + + public async virtual Task> GetListAsync(EntityChangeGetByPagedDto input) + { + var totalCount = await EntityChangeStore.GetCountAsync( + input.AuditLogId, input.StartTime, input.EndTime, + input.ChangeType, input.EntityId, input.EntityTypeFullName); + + var entityChanges = await EntityChangeStore.GetListAsync( + input.Sorting, input.MaxResultCount, input.SkipCount, + input.AuditLogId, input.StartTime, input.EndTime, + input.ChangeType, input.EntityId, input.EntityTypeFullName); + + return new PagedResultDto(totalCount, + ObjectMapper.Map, List>(entityChanges)); + } + + public async virtual Task GetWithUsernameAsync(Guid id) + { + var entityChangeWithUsername = await EntityChangeStore.GetWithUsernameAsync(id); + + return ObjectMapper.Map(entityChangeWithUsername); + } + + public async virtual Task> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) + { + var entityChangeWithUsernames = await EntityChangeStore.GetWithUsernameAsync( + input.EntityId, input.EntityTypeFullName); + + return new ListResultDto( + ObjectMapper.Map, List>(entityChangeWithUsernames)); + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditingApplicationServiceBase.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditingApplicationServiceBase.cs new file mode 100644 index 0000000..9cf36b4 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/AuditingApplicationServiceBase.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Application.Services; +using Volo.Abp.AuditLogging.Localization; + +namespace Sanhe.Abp.Auditing +{ + public abstract class AuditingApplicationServiceBase : ApplicationService + { + protected AuditingApplicationServiceBase() + { + LocalizationResource = typeof(AuditLoggingResource); + ObjectMapperContext = typeof(AbpAuditingApplicationModule); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/Logging/LogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/Logging/LogAppService.cs new file mode 100644 index 0000000..c4a90f1 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/Logging/LogAppService.cs @@ -0,0 +1,49 @@ +using Sanhe.Abp.Auditing.Logging.Dto; +using Sanhe.Abp.Logging; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace Sanhe.Abp.Auditing.Logging +{ + public class LogAppService : AuditingApplicationServiceBase, ILogAppService + { + private readonly ILoggingManager _manager; + + public LogAppService(ILoggingManager manager) + { + _manager = manager; + } + + public virtual async Task GetAsync(string id) + { + var log = await _manager.GetAsync(id); + + return ObjectMapper.Map(log); + } + + public virtual async Task> GetListAsync(LogGetByPagedDto input) + { + var count = await _manager.GetCountAsync( + input.StartTime, input.EndTime, input.Level, + input.MachineName, input.Environment, + input.Application, input.Context, + input.RequestId, input.RequestPath, + input.CorrelationId, input.ProcessId, + input.ThreadId, input.HasException); + + var logs = await _manager.GetListAsync( + input.Sorting, input.MaxResultCount, input.SkipCount, + input.StartTime, input.EndTime, input.Level, + input.MachineName, input.Environment, + input.Application, input.Context, + input.RequestId, input.RequestPath, + input.CorrelationId, input.ProcessId, + input.ThreadId, input.HasException, + includeDetails: false); + + return new PagedResultDto(count, + ObjectMapper.Map, List>(logs)); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogAppService.cs b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogAppService.cs new file mode 100644 index 0000000..49a95da --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.Application/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogAppService.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Authorization; +using Sanhe.Abp.Auditing.Features; +using Sanhe.Abp.Auditing.Permissions; +using Sanhe.Abp.AuditLogging; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Features; + +namespace Sanhe.Abp.Auditing.SecurityLogs +{ + [Authorize(AuditingPermissionNames.SecurityLog.Default)] + [RequiresFeature(AuditingFeatureNames.Logging.SecurityLog)] + public class SecurityLogAppService : AuditingApplicationServiceBase, ISecurityLogAppService + { + protected ISecurityLogManager SecurityLogManager { get; } + + public SecurityLogAppService(ISecurityLogManager securityLogManager) + { + SecurityLogManager = securityLogManager; + } + + public virtual async Task GetAsync(Guid id) + { + var securityLog = await SecurityLogManager.GetAsync(id, includeDetails: true); + + return ObjectMapper.Map(securityLog); + } + + public virtual async Task> GetListAsync(SecurityLogGetByPagedDto input) + { + var securityLogCount = await SecurityLogManager + .GetCountAsync(input.StartTime, input.EndTime, + input.ApplicationName, input.Identity, input.ActionName, + input.UserId, input.UserName, input.ClientId, input.CorrelationId + ); + + var securityLogs = await SecurityLogManager + .GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, + input.StartTime, input.EndTime, + input.ApplicationName, input.Identity, input.ActionName, + input.UserId, input.UserName, input.ClientId, input.CorrelationId, + includeDetails: false + ); + + return new PagedResultDto(securityLogCount, + ObjectMapper.Map, List>(securityLogs)); + } + + [Authorize(AuditingPermissionNames.SecurityLog.Delete)] + public virtual async Task DeleteAsync(Guid id) + { + await SecurityLogManager.DeleteAsync(id); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/FodyWeavers.xml b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe.Abp.Auditing.HttpApi.csproj b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe.Abp.Auditing.HttpApi.csproj new file mode 100644 index 0000000..d356b6a --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe.Abp.Auditing.HttpApi.csproj @@ -0,0 +1,19 @@ + + + + + + + net6.0 + + + + + + + + + + + + diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AbpAuditingHttpApiModule.cs b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AbpAuditingHttpApiModule.cs new file mode 100644 index 0000000..468ff15 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AbpAuditingHttpApiModule.cs @@ -0,0 +1,41 @@ +using Localization.Resources.AbpUi; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.AuditLogging.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.Auditing +{ + [DependsOn( + typeof(AbpAspNetCoreMvcModule), + typeof(AbpAuditingApplicationContractsModule))] + public class AbpAuditingHttpApiModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAuditingHttpApiModule).Assembly); + }); + + PreConfigure(options => + { + options.AddAssemblyResource(typeof(AuditLoggingResource), typeof(AbpAuditingApplicationContractsModule).Assembly); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpUiResource) + ); + }); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/AuditLogController.cs b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/AuditLogController.cs new file mode 100644 index 0000000..f201572 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/AuditLogController.cs @@ -0,0 +1,61 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace Sanhe.Abp.Auditing.AuditLogs +{ + /// + /// 审计日志 + /// + [RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] + [Area("auditing")] + [ControllerName("audit-log")] + [Route("api/auditing/audit-log")] + public class AuditLogController : AbpController, IAuditLogAppService + { + protected IAuditLogAppService AuditLogAppService { get; } + + public AuditLogController(IAuditLogAppService auditLogAppService) + { + AuditLogAppService = auditLogAppService; + } + + /// + /// 根据Id获取 + /// + /// + /// + [HttpGet] + [Route("{id}")] + public virtual Task GetAsync(Guid id) + { + return AuditLogAppService.GetAsync(id); + } + + /// + /// 分页获取 + /// + /// + /// + [HttpGet] + public virtual Task> GetListAsync(AuditLogGetByPagedDto input) + { + return AuditLogAppService.GetListAsync(input); + } + + /// + /// 根据Id删除 + /// + /// + /// + [HttpDelete] + [Route("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return AuditLogAppService.DeleteAsync(id); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/EntityChangesController.cs b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/EntityChangesController.cs new file mode 100644 index 0000000..7145ed3 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/AuditLogs/EntityChangesController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace Sanhe.Abp.Auditing.AuditLogs; + +[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] +[Area("auditing")] +[ControllerName("entity-changes")] +[Route("api/auditing/entity-changes")] +public class EntityChangesController : AbpControllerBase, IEntityChangesAppService +{ + protected IEntityChangesAppService EntityChangeAppService { get; } + + public EntityChangesController(IEntityChangesAppService entityChangeAppService) + { + EntityChangeAppService = entityChangeAppService; + } + + [HttpGet] + [Route("{id}")] + public Task GetAsync(Guid id) + { + return EntityChangeAppService.GetAsync(id); + } + + [HttpGet] + public Task> GetListAsync(EntityChangeGetByPagedDto input) + { + return EntityChangeAppService.GetListAsync(input); + } + + [HttpGet] + [Route("with-username/{id}")] + public Task GetWithUsernameAsync(Guid id) + { + return EntityChangeAppService.GetWithUsernameAsync(id); + } + + [HttpGet] + [Route("with-username")] + public Task> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) + { + return EntityChangeAppService.GetWithUsernameAsync(input); + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/Logging/LogController.cs b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/Logging/LogController.cs new file mode 100644 index 0000000..b7bde9b --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/Logging/LogController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc; +using Sanhe.Abp.Auditing.Logging.Dto; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace Sanhe.Abp.Auditing.Logging +{ + /// + /// 日志 + /// + [RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] + [Area("auditing")] + [ControllerName("logging")] + [Route("api/auditing/logging")] + public class LogController : AbpController, ILogAppService + { + private readonly ILogAppService _service; + + public LogController(ILogAppService service) + { + _service = service; + } + + /// + /// 根据Id获取 + /// + /// + /// + [HttpGet] + [Route("{id}")] + public virtual Task GetAsync(string id) + { + return _service.GetAsync(id); + } + + /// + /// 分页获取 + /// + /// + /// + [HttpGet] + public virtual Task> GetListAsync(LogGetByPagedDto input) + { + return _service.GetListAsync(input); + } + } +} diff --git a/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogController.cs b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogController.cs new file mode 100644 index 0000000..59332b8 --- /dev/null +++ b/modules/auditing/Sanhe.Abp.Auditing.HttpApi/Sanhe/Abp/Auditing/SecurityLogs/SecurityLogController.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace Sanhe.Abp.Auditing.SecurityLogs +{ + /// + /// 安全日志 + /// + [RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] + [Area("auditing")] + [ControllerName("security-log")] + [Route("api/auditing/security-log")] + public class SecurityLogController : AbpController, ISecurityLogAppService + { + protected ISecurityLogAppService SecurityLogAppService { get; } + + public SecurityLogController(ISecurityLogAppService securityLogAppService) + { + SecurityLogAppService = securityLogAppService; + } + + /// + /// 根据Id获取 + /// + /// + /// + [HttpGet] + [Route("{id}")] + public virtual Task GetAsync(Guid id) + { + return SecurityLogAppService.GetAsync(id); + } + + /// + /// 分页获取 + /// + /// + /// + [HttpGet] + public virtual Task> GetListAsync(SecurityLogGetByPagedDto input) + { + return SecurityLogAppService.GetListAsync(input); + } + + /// + /// 根据Id删除 + /// + /// + /// + [HttpDelete] + [Route("{id}")] + public virtual Task DeleteAsync(Guid id) + { + return SecurityLogAppService.DeleteAsync(id); + } + + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/FodyWeavers.xml b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/README.md b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/README.md new file mode 100644 index 0000000..0310e60 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/README.md @@ -0,0 +1,32 @@ +# LINGYUN.Abp.Logging.Serilog.Elasticsearch + +ILoggingManager 接口的ES实现, 从ES中检索日志信息 + +## 模块引用 + +```csharp +[DependsOn(typeof(AbpLoggingSerilogElasticsearchModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` + +## 配置项 + +* AbpLoggingSerilogElasticsearchOptions.IndexFormat 必须和Serilog配置项中的IndexFormat相同,否则无法定位到正确的索引 + +## appsettings.json + +```json +{ + "Logging": { + "Serilog": { + "Elasticsearch": { + "IndexFormat": "logstash-{0:yyyy.MM.dd}" + } + } + } +} + +``` \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe.Abp.Logging.Serilog.Elasticsearch.csproj b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe.Abp.Logging.Serilog.Elasticsearch.csproj new file mode 100644 index 0000000..651664e --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe.Abp.Logging.Serilog.Elasticsearch.csproj @@ -0,0 +1,24 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + + + + + diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchMapperProfile.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchMapperProfile.cs new file mode 100644 index 0000000..af442aa --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchMapperProfile.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using Sanhe.Abp.Logging; + +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + public class AbpLoggingSerilogElasticsearchMapperProfile : Profile + { + public AbpLoggingSerilogElasticsearchMapperProfile() + { + CreateMap(); + CreateMap() + .ForMember(log => log.Id, map => map.MapFrom(slog => slog.UniqueId.ToString())); + CreateMap(); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchModule.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchModule.cs new file mode 100644 index 0000000..eeaeb79 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchModule.cs @@ -0,0 +1,31 @@ +using Sanhe.Abp.Elasticsearch; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Json; +using Volo.Abp.Modularity; +using Sanhe.Abp.Logging; + +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + [DependsOn( + typeof(AbpLoggingModule), + typeof(AbpElasticsearchModule), + typeof(AbpAutoMapperModule), + typeof(AbpJsonModule))] + public class AbpLoggingSerilogElasticsearchModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + Configure(configuration.GetSection("Logging:Serilog:Elasticsearch")); + + context.Services.AddAutoMapperObjectMapper(); + + Configure(options => + { + options.AddProfile(validate: true); + }); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchOptions.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchOptions.cs new file mode 100644 index 0000000..7a1b812 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/AbpLoggingSerilogElasticsearchOptions.cs @@ -0,0 +1,12 @@ +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + public class AbpLoggingSerilogElasticsearchOptions + { + public string IndexFormat { get; set; } + + public AbpLoggingSerilogElasticsearchOptions() + { + IndexFormat = "logstash-{0:yyyy.MM.dd}"; + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogElasticsearchLoggingManager.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogElasticsearchLoggingManager.cs new file mode 100644 index 0000000..63b33bd --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogElasticsearchLoggingManager.cs @@ -0,0 +1,412 @@ +using Sanhe.Abp.Elasticsearch; +using Sanhe.Abp.Serilog.Enrichers.Application; +using Sanhe.Abp.Serilog.Enrichers.UniqueId; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Nest; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectMapping; +using Sanhe.Abp.Logging; + +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + [Dependency(ReplaceServices = true)] + public class SerilogElasticsearchLoggingManager : ILoggingManager, ISingletonDependency + { + private static readonly Regex IndexFormatRegex = new Regex(@"^(.*)(?:\{0\:.+\})(.*)$"); + + private readonly IObjectMapper _objectMapper; + private readonly ICurrentTenant _currentTenant; + private readonly AbpLoggingSerilogElasticsearchOptions _options; + private readonly IElasticsearchClientFactory _clientFactory; + + public ILogger Logger { protected get; set; } + + public SerilogElasticsearchLoggingManager( + IObjectMapper objectMapper, + ICurrentTenant currentTenant, + IOptions options, + IElasticsearchClientFactory clientFactory) + { + _objectMapper = objectMapper; + _currentTenant = currentTenant; + _clientFactory = clientFactory; + _options = options.Value; + + Logger = NullLogger.Instance; + } + + /// + /// + /// + /// 时间类型或者转换为timestamp都可以查询 + /// + /// + public virtual async Task GetAsync( + string id, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + ISearchResponse response; + + if (_currentTenant.IsAvailable) + { + /* + "query": { + "bool": { + "must": [ + { + "term": { + "fields.TenantId.keyword": { + "value": _currentTenant.GetId() + } + } + }, + { + "term": { + "fields.UniqueId": { + "value": "1474021081433481216" + } + } + } + ] + } + } + */ + response = await client.SearchAsync( + dsl => + dsl.Index(CreateIndex()) + .Query( + (q) => q.Bool( + (b) => b.Must( + (s) => s.Term( + (t) => t.Field(GetField(nameof(SerilogInfo.Fields.UniqueId))).Value(id)), + (s) => s.Term( + (t) => t.Field(GetField(nameof(SerilogInfo.Fields.TenantId))).Value(_currentTenant.GetId()))))), + cancellationToken); + } + else + { + /* + "query": { + "bool": { + "must": [ + { + "term": { + "fields.UniqueId": { + "value": "1474021081433481216" + } + } + } + ] + } + } + */ + response = await client.SearchAsync( + dsl => + dsl.Index(CreateIndex()) + .Query( + (q) => q.Bool( + (b) => b.Must( + (s) => s.Term( + (t) => t.Field(GetField(nameof(SerilogInfo.Fields.UniqueId))).Value(id))))), + cancellationToken); + } + + return _objectMapper.Map(response.Documents.FirstOrDefault()); + } + + public virtual async Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + Microsoft.Extensions.Logging.LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + level, + machineName, + environment, + application, + context, + requestId, + requestPath, + correlationId, + processId, + threadId, + hasException); + + var response = await client.CountAsync((dsl) => + dsl.Index(CreateIndex()) + .Query(log => log.Bool(b => b.Must(querys.ToArray()))), + cancellationToken); + + return response.Count; + } + + /// + /// 获取日志列表 + /// + /// 排序字段 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + Microsoft.Extensions.Logging.LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var client = _clientFactory.Create(); + + var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) + ? SortOrder.Ascending : SortOrder.Descending; + sorting = !sorting.IsNullOrWhiteSpace() + ? sorting.Split()[0] + : nameof(SerilogInfo.TimeStamp); + + var querys = BuildQueryDescriptor( + startTime, + endTime, + level, + machineName, + environment, + application, + context, + requestId, + requestPath, + correlationId, + processId, + threadId, + hasException); + + SourceFilterDescriptor SourceFilter(SourceFilterDescriptor selector) + { + selector.IncludeAll(); + if (!includeDetails) + { + selector.Excludes(field => + field.Field("exceptions")); + } + + return selector; + } + + var response = await client.SearchAsync((dsl) => + dsl.Index(CreateIndex()) + .Query(log => + log.Bool(b => + b.Must(querys.ToArray()))) + .Source(SourceFilter) + .Sort(log => log.Field(GetField(sorting), sortOrder)) + .From(skipCount) + .Size(maxResultCount), + cancellationToken); + + return _objectMapper.Map, List>(response.Documents.ToList()); + } + + protected virtual List, QueryContainer>> BuildQueryDescriptor( + DateTime? startTime = null, + DateTime? endTime = null, + Microsoft.Extensions.Logging.LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null) + { + var querys = new List, QueryContainer>>(); + + if (_currentTenant.IsAvailable) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.TenantId))).Value(_currentTenant.GetId()))); + } + if (startTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SerilogInfo.TimeStamp))).GreaterThanOrEquals(startTime))); + } + if (endTime.HasValue) + { + querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SerilogInfo.TimeStamp))).LessThanOrEquals(endTime))); + } + if (level.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Level))).Value(level.ToString()))); + } + if (!machineName.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.MachineName))).Value(machineName))); + } + if (!environment.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Environment))).Value(environment))); + } + if (!application.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Application))).Value(application))); + } + if (!context.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Context))).Value(context))); + } + if (!requestId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.RequestId))).Value(requestId))); + } + if (!requestPath.IsNullOrWhiteSpace()) + { + // 模糊匹配 + querys.Add((log) => log.MatchPhrasePrefix((q) => q.Field(f => f.Fields.RequestPath).Query(requestPath))); + } + if (!correlationId.IsNullOrWhiteSpace()) + { + querys.Add((log) => log.MatchPhrase((q) => q.Field(GetField(nameof(SerilogInfo.Fields.CorrelationId))).Query(correlationId))); + } + if (processId.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.ProcessId))).Value(processId))); + } + if (threadId.HasValue) + { + querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.ThreadId))).Value(threadId))); + } + + if (hasException.HasValue) + { + if (hasException.Value) + { + /* 存在exceptions字段则就是有异常信息 + * "exists": { + "field": "exceptions" + } + */ + querys.Add( + (q) => q.Exists( + (e) => e.Field("exceptions"))); + } + else + { + // 不存在 exceptions字段就是没有异常信息的消息 + /* + * "bool": { + "must_not": [ + { + "exists": { + "field": "exceptions" + } + } + ] + } + */ + querys.Add( + (q) => q.Bool( + (b) => b.MustNot( + (m) => m.Exists( + (e) => e.Field("exceptions"))))); + } + } + + return querys; + } + + protected virtual string CreateIndex(DateTimeOffset? offset = null) + { + if (!offset.HasValue) + { + return IndexFormatRegex.Replace(_options.IndexFormat, @"$1*$2"); + } + return string.Format(_options.IndexFormat, offset.Value).ToLowerInvariant(); + } + + private readonly static IDictionary _fieldMaps = new Dictionary(StringComparer.InvariantCultureIgnoreCase) + { + { "timestamp", "@timestamp" }, + { "level", "level.keyword" }, + { "machinename", $"fields.{AbpLoggingEnricherPropertyNames.MachineName}.keyword" }, + { "environment", $"fields.{AbpLoggingEnricherPropertyNames.EnvironmentName}.keyword" }, + { "application", $"fields.{AbpSerilogEnrichersConsts.ApplicationNamePropertyName}.keyword" }, + { "context", "fields.Context.keyword" }, + { "actionid", "fields.ActionId.keyword" }, + { "actionname", "fields.ActionName.keyword" }, + { "requestid", "fields.RequestId.keyword" }, + { "requestpath", "fields.RequestPath.keyword" }, + { "connectionid", "fields.ConnectionId.keyword" }, + { "correlationid", "fields.CorrelationId.keyword" }, + { "clientid", "fields.ClientId.keyword" }, + { "userid", "fields.UserId.keyword" }, + { "processid", "fields.ProcessId" }, + { "threadid", "fields.ThreadId" }, + { "id", $"fields.{AbpSerilogUniqueIdConsts.UniqueIdPropertyName}" }, + { "uniqueid", $"fields.{AbpSerilogUniqueIdConsts.UniqueIdPropertyName}" }, + }; + protected virtual string GetField(string field) + { + foreach (var fieldMap in _fieldMaps) + { + if (field.ToLowerInvariant().Contains(fieldMap.Key)) + { + return fieldMap.Value; + } + } + + return field; + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogException.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogException.cs new file mode 100644 index 0000000..84af6a8 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogException.cs @@ -0,0 +1,26 @@ +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + public class SerilogException + { + [Nest.PropertyName("SourceContext")] + public int Depth { get; set; } + + [Nest.PropertyName("ClassName")] + public string Class { get; set; } + + [Nest.PropertyName("Message")] + public string Message { get; set; } + + [Nest.PropertyName("Source")] + public string Source { get; set; } + + [Nest.PropertyName("StackTraceString")] + public string StackTrace { get; set; } + + [Nest.PropertyName("HResult")] + public int HResult { get; set; } + + [Nest.PropertyName("HelpURL")] + public string HelpURL { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogField.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogField.cs new file mode 100644 index 0000000..0e81266 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogField.cs @@ -0,0 +1,58 @@ +using Sanhe.Abp.Logging; +using Sanhe.Abp.Serilog.Enrichers.Application; +using Sanhe.Abp.Serilog.Enrichers.UniqueId; +using System; + +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + public class SerilogField + { + [Nest.PropertyName(AbpSerilogUniqueIdConsts.UniqueIdPropertyName)] + public long UniqueId { get; set; } + + [Nest.PropertyName(AbpLoggingEnricherPropertyNames.MachineName)] + public string MachineName { get; set; } + + [Nest.PropertyName(AbpLoggingEnricherPropertyNames.EnvironmentName)] + public string Environment { get; set; } + + [Nest.PropertyName(AbpSerilogEnrichersConsts.ApplicationNamePropertyName)] + public string Application { get; set; } + + [Nest.PropertyName("SourceContext")] + public string Context { get; set; } + + [Nest.PropertyName("ActionId")] + public string ActionId { get; set; } + + [Nest.PropertyName("ActionName")] + public string ActionName { get; set; } + + [Nest.PropertyName("RequestId")] + public string RequestId { get; set; } + + [Nest.PropertyName("RequestPath")] + public string RequestPath { get; set; } + + [Nest.PropertyName("ConnectionId")] + public string ConnectionId { get; set; } + + [Nest.PropertyName("CorrelationId")] + public string CorrelationId { get; set; } + + [Nest.PropertyName("ClientId")] + public string ClientId { get; set; } + + [Nest.PropertyName("UserId")] + public string UserId { get; set; } + + [Nest.PropertyName("TenantId")] + public Guid? TenantId { get; set; } + + [Nest.PropertyName("ProcessId")] + public int ProcessId { get; set; } + + [Nest.PropertyName("ThreadId")] + public int ThreadId { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogInfo.cs b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogInfo.cs new file mode 100644 index 0000000..99ea6c2 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging.Serilog.Elasticsearch/Sanhe/Abp/AuditLogging/Serilog/Elasticsearch/SerilogInfo.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Logging; +using Serilog.Formatting.Elasticsearch; +using System; +using System.Collections.Generic; + +namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch +{ + [Serializable] + public class SerilogInfo + { + [Nest.PropertyName(ElasticsearchJsonFormatter.TimestampPropertyName)] + public DateTime TimeStamp { get; set; } + + [Nest.PropertyName(ElasticsearchJsonFormatter.LevelPropertyName)] + public LogLevel Level { get; set; } + + [Nest.PropertyName(ElasticsearchJsonFormatter.RenderedMessagePropertyName)] + public string Message { get; set; } + + [Nest.PropertyName("fields")] + public SerilogField Fields { get; set; } + + [Nest.PropertyName("exceptions")] + public List Exceptions { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/FodyWeavers.xml b/modules/logging/Sanhe.Abp.Logging/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Logging/README.md b/modules/logging/Sanhe.Abp.Logging/README.md new file mode 100644 index 0000000..5f8b185 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/README.md @@ -0,0 +1,15 @@ +# LINGYUN.Abp.Logging + +日志基础模块 + +定义 ILoggingManager 接口, 实现日志信息查询 + +## 模块引用 + +```csharp +[DependsOn(typeof(AbpLoggingModule))] +public class YouProjectModule : AbpModule +{ + // other +} +``` diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe.Abp.Logging.csproj b/modules/logging/Sanhe.Abp.Logging/Sanhe.Abp.Logging.csproj new file mode 100644 index 0000000..a008e21 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe.Abp.Logging.csproj @@ -0,0 +1,14 @@ + + + + + + + netstandard2.0 + + + + + + + diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingEnricherPropertyNames.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingEnricherPropertyNames.cs new file mode 100644 index 0000000..e41ed6b --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingEnricherPropertyNames.cs @@ -0,0 +1,8 @@ +namespace Sanhe.Abp.Logging +{ + public class AbpLoggingEnricherPropertyNames + { + public const string MachineName = "MachineName"; + public const string EnvironmentName = "EnvironmentName"; + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingModule.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingModule.cs new file mode 100644 index 0000000..525e6b8 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/AbpLoggingModule.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.Logging +{ + public class AbpLoggingModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var configuration = context.Services.GetConfiguration(); + + Configure(configuration.GetSection("Logging")); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/DefaultLoggingManager.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/DefaultLoggingManager.cs new file mode 100644 index 0000000..8ab015a --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/DefaultLoggingManager.cs @@ -0,0 +1,72 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Sanhe.Abp.Logging +{ + [Dependency(TryRegister = true)] + public class DefaultLoggingManager : ILoggingManager, ISingletonDependency + { + public ILogger Logger { protected get; set; } + + public DefaultLoggingManager() + { + Logger = NullLogger.Instance; + } + + public Task GetAsync(string id, CancellationToken cancellationToken = default) + { + Logger.LogDebug("No logging manager is available!"); + LogInfo logInfo = null; + return Task.FromResult(logInfo); + } + + public Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No logging manager is available!"); + return Task.FromResult(0L); + } + + public Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + Logger.LogDebug("No logging manager is available!"); + return Task.FromResult(new List()); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/ILoggingManager.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/ILoggingManager.cs new file mode 100644 index 0000000..018dd54 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/ILoggingManager.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Sanhe.Abp.Logging +{ + public interface ILoggingManager + { + Task GetAsync( + string id, + CancellationToken cancellationToken = default); + + Task GetCountAsync( + DateTime? startTime = null, + DateTime? endTime = null, + LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = 50, + int skipCount = 0, + DateTime? startTime = null, + DateTime? endTime = null, + LogLevel? level = null, + string machineName = null, + string environment = null, + string application = null, + string context = null, + string requestId = null, + string requestPath = null, + string correlationId = null, + int? processId = null, + int? threadId = null, + bool? hasException = null, + bool includeDetails = false, + CancellationToken cancellationToken = default); + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogException.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogException.cs new file mode 100644 index 0000000..2759df1 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogException.cs @@ -0,0 +1,13 @@ +namespace Sanhe.Abp.Logging +{ + public class LogException + { + public int Depth { get; set; } + public string Class { get; set; } + public string Message { get; set; } + public string Source { get; set; } + public string StackTrace { get; set; } + public int HResult { get; set; } + public string HelpURL { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogField.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogField.cs new file mode 100644 index 0000000..474579a --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogField.cs @@ -0,0 +1,21 @@ +namespace Sanhe.Abp.Logging +{ + public class LogField + { + public string Id { get; set; } + public string MachineName { get; set; } + public string Environment { get; set; } + public string Application { get; set; } + public string Context { get; set; } + public string ActionId { get; set; } + public string ActionName { get; set; } + public string RequestId { get; set; } + public string RequestPath { get; set; } + public string ConnectionId { get; set; } + public string CorrelationId { get; set; } + public string ClientId { get; set; } + public string UserId { get; set; } + public int ProcessId { get; set; } + public int ThreadId { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogInfo.cs b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogInfo.cs new file mode 100644 index 0000000..d17a20f --- /dev/null +++ b/modules/logging/Sanhe.Abp.Logging/Sanhe/Abp/Logging/LogInfo.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; + +namespace Sanhe.Abp.Logging +{ + public class LogInfo + { + public DateTime TimeStamp { get; set; } + public LogLevel Level { get; set; } + public string Message { get; set; } + public LogField Fields { get; set; } + public List Exceptions { get; set; } + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/FodyWeavers.xml b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/README.md b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/README.md new file mode 100644 index 0000000..87c5b4e --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/README.md @@ -0,0 +1,48 @@ +# LINGYUN.Abp.Serilog.Enrichers.Application + +日志属性添加应用程序标识 + +## 模块引用 + +```csharp +[DependsOn(typeof(AbpSerilogEnrichersApplicationModule))] +public class YouProjectModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AbpSerilogEnrichersConsts.ApplicationName = "demo-app"; + } +} +``` + +## 配置项 + +以下为字段常量,需要明确变更 + +* AbpSerilogEnrichersConsts.ApplicationNamePropertyName 用于自定义ApplicationName字段的名称 +* AbpSerilogEnrichersConsts.ApplicationName 在日志中标识当前应用的名称 + +## How to Use + +```csharp + +Log.Logger = new LoggerConfiguration() + .Enrich.WithApplicationName() + // ...other configuration... + .CreateLogger(); + +``` +**Or** + +```json + +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information" + }, + "Enrich": [ "WithApplicationName" ] + } +} + +``` \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe.Abp.Serilog.Enrichers.Application.csproj b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe.Abp.Serilog.Enrichers.Application.csproj new file mode 100644 index 0000000..fdfadab --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe.Abp.Serilog.Enrichers.Application.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersApplicationModule.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersApplicationModule.cs new file mode 100644 index 0000000..c792e8b --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersApplicationModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.Serilog.Enrichers.Application +{ + public class AbpSerilogEnrichersApplicationModule : AbpModule + { + + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersConsts.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersConsts.cs new file mode 100644 index 0000000..669cdaa --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/AbpSerilogEnrichersConsts.cs @@ -0,0 +1,8 @@ +namespace Sanhe.Abp.Serilog.Enrichers.Application +{ + public class AbpSerilogEnrichersConsts + { + public const string ApplicationNamePropertyName = "ApplicationName"; + public static string ApplicationName { get; set; } = "app"; + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/ApplicationNameEnricher.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/ApplicationNameEnricher.cs new file mode 100644 index 0000000..c7e7d68 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Sanhe/Abp/Serilog/Enrichers/Application/ApplicationNameEnricher.cs @@ -0,0 +1,30 @@ +using Serilog.Core; +using Serilog.Events; + +namespace Sanhe.Abp.Serilog.Enrichers.Application +{ + public class ApplicationNameEnricher : ILogEventEnricher + { + private LogEventProperty _cachedProperty; + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + logEvent.AddPropertyIfAbsent(GetLogEventProperty(propertyFactory)); + } + + private LogEventProperty GetLogEventProperty(ILogEventPropertyFactory propertyFactory) + { + if (_cachedProperty == null) + _cachedProperty = CreateProperty(propertyFactory); + + return _cachedProperty; + } + + private static LogEventProperty CreateProperty(ILogEventPropertyFactory propertyFactory) + { + return propertyFactory.CreateProperty( + AbpSerilogEnrichersConsts.ApplicationNamePropertyName, + AbpSerilogEnrichersConsts.ApplicationName); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Serilog/ApplicationLoggerConfigurationExtensions.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Serilog/ApplicationLoggerConfigurationExtensions.cs new file mode 100644 index 0000000..f376cf1 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.Application/Serilog/ApplicationLoggerConfigurationExtensions.cs @@ -0,0 +1,16 @@ +using Sanhe.Abp.Serilog.Enrichers.Application; +using Serilog.Configuration; +using System; + +namespace Serilog +{ + public static class ApplicationLoggerConfigurationExtensions + { + public static LoggerConfiguration WithApplicationName( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException(nameof(enrichmentConfiguration)); + return enrichmentConfiguration.With(); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/FodyWeavers.xml b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/FodyWeavers.xml new file mode 100644 index 0000000..00e1d9a --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj new file mode 100644 index 0000000..2203494 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj @@ -0,0 +1,19 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdModule.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdModule.cs new file mode 100644 index 0000000..3e04b5f --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Sanhe.Abp.IdGenerator; +using Sanhe.Abp.IdGenerator.Snowflake; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.Serilog.Enrichers.UniqueId +{ + [DependsOn(typeof(AbpIdGeneratorModule))] + public class AbpSerilogEnrichersUniqueIdModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var options = context.Services.ExecutePreConfiguredActions(); + UniqueIdEnricher.DistributedIdGenerator = SnowflakeIdGenerator.Create(options.SnowflakeIdOptions); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdOptions.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdOptions.cs new file mode 100644 index 0000000..855578e --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogEnrichersUniqueIdOptions.cs @@ -0,0 +1,12 @@ +using Sanhe.Abp.IdGenerator.Snowflake; + +namespace Sanhe.Abp.Serilog.Enrichers.UniqueId; + +public class AbpSerilogEnrichersUniqueIdOptions +{ + public SnowflakeIdOptions SnowflakeIdOptions { get; set; } + public AbpSerilogEnrichersUniqueIdOptions() + { + SnowflakeIdOptions = new SnowflakeIdOptions(); + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogUniqueIdConsts.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogUniqueIdConsts.cs new file mode 100644 index 0000000..de81b28 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/AbpSerilogUniqueIdConsts.cs @@ -0,0 +1,7 @@ +namespace Sanhe.Abp.Serilog.Enrichers.UniqueId +{ + public class AbpSerilogUniqueIdConsts + { + public const string UniqueIdPropertyName = "UniqueId"; + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/UniqueIdEnricher.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/UniqueIdEnricher.cs new file mode 100644 index 0000000..46fbcb2 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Sanhe/Abp/Serilog/Enrichers/UniqueId/UniqueIdEnricher.cs @@ -0,0 +1,19 @@ +using Sanhe.Abp.IdGenerator; +using Serilog.Core; +using Serilog.Events; + +namespace Sanhe.Abp.Serilog.Enrichers.UniqueId +{ + public class UniqueIdEnricher : ILogEventEnricher + { + internal static IDistributedIdGenerator DistributedIdGenerator; + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + logEvent.AddOrUpdateProperty( + propertyFactory.CreateProperty( + AbpSerilogUniqueIdConsts.UniqueIdPropertyName, + DistributedIdGenerator.Create())); + } + } +} diff --git a/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Serilog/UniqueIdLoggerConfigurationExtensions.cs b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Serilog/UniqueIdLoggerConfigurationExtensions.cs new file mode 100644 index 0000000..769eb81 --- /dev/null +++ b/modules/logging/Sanhe.Abp.Serilog.Enrichers.UniqueId/Serilog/UniqueIdLoggerConfigurationExtensions.cs @@ -0,0 +1,16 @@ +using Sanhe.Abp.Serilog.Enrichers.UniqueId; +using Serilog.Configuration; +using System; + +namespace Serilog +{ + public static class UniqueIdLoggerConfigurationExtensions + { + public static LoggerConfiguration WithUniqueId( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException(nameof(enrichmentConfiguration)); + return enrichmentConfiguration.With(); + } + } +}