119 changed files with 5124 additions and 0 deletions
			
			
		| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -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" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| @ -0,0 +1,20 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\elasticsearch\Sanhe.Abp.Elasticsearch\Sanhe.Abp.Elasticsearch.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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<AbpAuditLoggingElasticsearchOptions>(configuration.GetSection("AuditLogging:Elasticsearch")); | ||||
| 
 | ||||
|             context.Services.AddHostedService<IndexInitializerService>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AbpExceptionHandlingOptions> exceptionHandlingOptions, | ||||
|             IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, | ||||
|             IJsonSerializer jsonSerializer) | ||||
|         { | ||||
|             GuidGenerator = guidGenerator; | ||||
|             ExceptionHandlingOptions = exceptionHandlingOptions.Value; | ||||
|             ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; | ||||
|             JsonSerializer = jsonSerializer; | ||||
|         } | ||||
| 
 | ||||
|         public virtual Task<AuditLog> 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<EntityChange>(); | ||||
| 
 | ||||
|             var actions = auditLogInfo | ||||
|                               .Actions? | ||||
|                               .Select(auditLogActionInfo => new AuditLogAction(GuidGenerator.Create(), auditLogId, auditLogActionInfo, tenantId: auditLogInfo.TenantId)) | ||||
|                               .ToList() | ||||
|                           ?? new List<AuditLogAction>(); | ||||
| 
 | ||||
|             var remoteServiceErrorInfos = auditLogInfo.Exceptions?.Select(exception => | ||||
|                 ExceptionToErrorInfoConverter.Convert(exception, options => | ||||
|                 { | ||||
|                     options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients; | ||||
|                     options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients; | ||||
|                 })) ?? new List<RemoteServiceErrorInfo>(); | ||||
| 
 | ||||
|             var exceptions = remoteServiceErrorInfos.Any() | ||||
|                 ? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true) | ||||
|                 : null; | ||||
| 
 | ||||
|             var comments = auditLogInfo | ||||
|                 .Comments? | ||||
|                 .JoinAsString(Environment.NewLine); | ||||
| 
 | ||||
|             var auditLog = new 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<ElasticsearchAuditLogManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public ElasticsearchAuditLogManager( | ||||
|             IIndexNameNormalizer indexNameNormalizer, | ||||
|             IOptions<AbpElasticsearchOptions> elasticsearchOptions, | ||||
|             IElasticsearchClientFactory clientFactory, | ||||
|             IOptions<AbpAuditingOptions> auditingOptions, | ||||
|             IAuditLogInfoToAuditLogConverter converter) | ||||
|         { | ||||
|             _converter = converter; | ||||
|             _clientFactory = clientFactory; | ||||
|             _auditingOptions = auditingOptions.Value; | ||||
|             _elasticsearchOptions = elasticsearchOptions.Value; | ||||
|             _indexNameNormalizer = indexNameNormalizer; | ||||
| 
 | ||||
|             Logger = NullLogger<ElasticsearchAuditLogManager>.Instance; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public virtual async Task<long> GetCountAsync( | ||||
|             DateTime? startTime = null, | ||||
|             DateTime? endTime = null, | ||||
|             string httpMethod = null, | ||||
|             string url = null, | ||||
|             Guid? userId = null, | ||||
|             string userName = null, | ||||
|             string applicationName = null, | ||||
|             string 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<AuditLog>(dsl => | ||||
|                 dsl.Index(CreateIndex()) | ||||
|                    .Query(log => log.Bool(b => b.Must(querys.ToArray()))), | ||||
|                 cancellationToken); | ||||
| 
 | ||||
|             return response.Count; | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<List<AuditLog>> 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<AuditLog> SourceFilter(SourceFilterDescriptor<AuditLog> 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<AuditLog>(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<AuditLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             var client = _clientFactory.Create(); | ||||
| 
 | ||||
|             var response = await client.GetAsync<AuditLog>( | ||||
|                 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<AuditLog>( | ||||
|                 id, | ||||
|                 dsl => | ||||
|                     dsl.Index(CreateIndex()), | ||||
|                 cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<string> 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<string> 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<Func<QueryContainerDescriptor<AuditLog>, 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<Func<QueryContainerDescriptor<AuditLog>, 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<string, string> _fieldMaps = new Dictionary<string, string>(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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<ElasticsearchEntityChangeStore> Logger { protected get; set; } | ||||
| 
 | ||||
|     public ElasticsearchEntityChangeStore( | ||||
|         IIndexNameNormalizer indexNameNormalizer, | ||||
|         IElasticsearchClientFactory clientFactory, | ||||
|         IOptions<AbpElasticsearchOptions> elasticsearchOptions) | ||||
|     { | ||||
|         _clientFactory = clientFactory; | ||||
|         _indexNameNormalizer = indexNameNormalizer; | ||||
|         _elasticsearchOptions = elasticsearchOptions.Value; | ||||
| 
 | ||||
|         Logger = NullLogger<ElasticsearchEntityChangeStore>.Instance; | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<EntityChange> GetAsync( | ||||
|         Guid entityChangeId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         var client = _clientFactory.Create(); | ||||
| 
 | ||||
|         var resposne = await client.SearchAsync<EntityChange>( | ||||
|             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<EntityChange>().FirstOrDefault(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<long> 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<QueryContainerDescriptor<EntityChange>, QueryContainer> selector = q => q.MatchAll(); | ||||
|         //if (querys.Count > 0) | ||||
|         //{ | ||||
|         //    selector = q => q.Bool(b => b.Must(querys.ToArray())); | ||||
|         //} | ||||
| 
 | ||||
|         //var response = await client.CountAsync<EntityChange>(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<List<EntityChange>> 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<EntityChange> SourceFilter(SourceFilterDescriptor<EntityChange> selector) | ||||
|         //{ | ||||
|         //    selector.Includes(GetEntityChangeSources()); | ||||
|         //    if (!includeDetails) | ||||
|         //    { | ||||
|         //        selector.Excludes(field => | ||||
|         //            field.Field("EntityChanges.PropertyChanges") | ||||
|         //                 .Field("EntityChanges.ExtraProperties")); | ||||
|         //    } | ||||
| 
 | ||||
|         //    return selector; | ||||
|         //} | ||||
| 
 | ||||
|         //Func<QueryContainerDescriptor<EntityChange>, QueryContainer> selector = q => q.MatchAll(); | ||||
|         //if (querys.Count > 0) | ||||
|         //{ | ||||
|         //    selector = q => q.Bool(b => b.Must(querys.ToArray())); | ||||
|         //} | ||||
| 
 | ||||
|         //var response = await client.SearchAsync<EntityChange>(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<EntityChange>().ToList(); | ||||
|         //    } | ||||
|         //} | ||||
|         await Task.CompletedTask; | ||||
|         return new List<EntityChange>(); | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<EntityChangeWithUsername> GetWithUsernameAsync( | ||||
|         Guid entityChangeId, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         var client = _clientFactory.Create(); | ||||
| 
 | ||||
|         var response = await client.SearchAsync<AuditLog>( | ||||
|             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<EntityChange>().FirstOrDefault(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return new EntityChangeWithUsername() | ||||
|         { | ||||
|             EntityChange = entityChange, | ||||
|             UserName = auditLog?.UserName | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<List<EntityChangeWithUsername>> GetWithUsernameAsync( | ||||
|         string entityId, | ||||
|         string entityTypeFullName, | ||||
|         CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         var client = _clientFactory.Create(); | ||||
| 
 | ||||
|         var response = await client.SearchAsync<AuditLog>( | ||||
|             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<EntityChange>().FirstOrDefault() | ||||
|                         : null | ||||
|                 }) | ||||
|                 .ToList(); | ||||
|         } | ||||
| 
 | ||||
|         return new List<EntityChangeWithUsername>(); | ||||
|     } | ||||
| 
 | ||||
|     protected virtual List<Func<QueryContainerDescriptor<EntityChange>, QueryContainer>> BuildQueryDescriptor( | ||||
|         Guid? auditLogId = null, | ||||
|         DateTime? startTime = null, | ||||
|         DateTime? endTime = null, | ||||
|         EntityChangeType? changeType = null, | ||||
|         string entityId = null, | ||||
|         string entityTypeFullName = null) | ||||
|     { | ||||
|         var querys = new List<Func<QueryContainerDescriptor<EntityChange>, 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<FieldsDescriptor<EntityChange>, IPromise<Fields>> 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<string, string> _fieldMaps = new Dictionary<string, string>(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(); | ||||
|     } | ||||
| } | ||||
| @ -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<ElasticsearchSecurityLogManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public ElasticsearchSecurityLogManager( | ||||
|             IGuidGenerator guidGenerator, | ||||
|             IIndexNameNormalizer indexNameNormalizer, | ||||
|             IOptions<AbpSecurityLogOptions> securityLogOptions, | ||||
|             IOptions<AbpElasticsearchOptions> elasticsearchOptions, | ||||
|             IElasticsearchClientFactory clientFactory) | ||||
|         { | ||||
|             _guidGenerator = guidGenerator; | ||||
|             _clientFactory = clientFactory; | ||||
|             _indexNameNormalizer = indexNameNormalizer; | ||||
|             _securityLogOptions = securityLogOptions.Value; | ||||
|             _elasticsearchOptions = elasticsearchOptions.Value; | ||||
| 
 | ||||
|             Logger = NullLogger<ElasticsearchSecurityLogManager>.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<SecurityLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             var client = _clientFactory.Create(); | ||||
| 
 | ||||
|             var response = await client.GetAsync<SecurityLog>( | ||||
|                 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<SecurityLog>( | ||||
|                 id, | ||||
|                 dsl => | ||||
|                     dsl.Index(CreateIndex()), | ||||
|                 cancellationToken); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<List<SecurityLog>> 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<SecurityLog>(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<long> 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<SecurityLog>(dsl => | ||||
|                 dsl.Index(CreateIndex()) | ||||
|                    .Query(log => log.Bool(b => b.Must(querys.ToArray()))), | ||||
|                 cancellationToken); | ||||
| 
 | ||||
|             return response.Count; | ||||
|         } | ||||
| 
 | ||||
|         protected virtual List<Func<QueryContainerDescriptor<SecurityLog>, 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<Func<QueryContainerDescriptor<SecurityLog>, 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<string, string> _fieldMaps = new Dictionary<string, string>(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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,10 @@ | ||||
| using System.Threading.Tasks; | ||||
| using Volo.Abp.Auditing; | ||||
| 
 | ||||
| namespace Sanhe.Abp.AuditLogging.Elasticsearch | ||||
| { | ||||
|     public interface IAuditLogInfoToAuditLogConverter | ||||
|     { | ||||
|         Task<AuditLog> ConvertAsync(AuditLogInfo auditLogInfo); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Sanhe.Abp.AuditLogging.Elasticsearch | ||||
| { | ||||
|     public interface IIndexInitializer | ||||
|     { | ||||
|         Task InitializeAsync(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| namespace Sanhe.Abp.AuditLogging.Elasticsearch | ||||
| { | ||||
|     public interface IIndexNameNormalizer | ||||
|     { | ||||
|         string NormalizeIndex(string index); | ||||
|     } | ||||
| } | ||||
| @ -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<IndexInitializer> Logger { protected get; set; } | ||||
| 
 | ||||
|         public IndexInitializer( | ||||
|             IOptions<AbpJsonOptions> jsonOptions, | ||||
|             IOptions<AbpAuditLoggingElasticsearchOptions> elasticsearchOptions, | ||||
|             IIndexNameNormalizer nameNormalizer, | ||||
|             IElasticsearchClientFactory clientFactory) | ||||
|         { | ||||
|             _jsonOptions = jsonOptions.Value; | ||||
|             _elasticsearchOptions = elasticsearchOptions.Value; | ||||
|             _nameNormalizer = nameNormalizer; | ||||
|             _clientFactory = clientFactory; | ||||
| 
 | ||||
|             Logger = NullLogger<IndexInitializer>.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<AuditLog>(map => | ||||
|                                 map.AutoMap() | ||||
|                                    .Properties(mp => | ||||
|                                        mp.Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat)) | ||||
|                                          .Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) | ||||
|                                          .Nested<EntityChange>(n => | ||||
|                                             n.AutoMap() | ||||
|                                              .Name(nameof(AuditLog.EntityChanges)) | ||||
|                                              .Properties(np => | ||||
|                                                 np.Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) | ||||
|                                                   .Date(p => p.Name(n => n.ChangeTime).Format(dateTimeFormat)) | ||||
|                                                   .Nested<EntityPropertyChange>(npn => npn.Name(nameof(EntityChange.PropertyChanges))))) | ||||
|                                          .Nested<AuditLogAction>(n => n.Name(nameof(AuditLog.Actions)) | ||||
|                                             .AutoMap() | ||||
|                                             .Properties(np => | ||||
|                                                 np.Object<ExtraPropertyDictionary>(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<SecurityLog>(map => | ||||
|                                     map.AutoMap() | ||||
|                                        .Properties(mp => | ||||
|                                             mp.Object<ExtraPropertyDictionary>(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()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AbpAuditLoggingElasticsearchOptions> 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}"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -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=*", | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| @ -0,0 +1,21 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>net6.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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<AbpAuditLoggingEntityFrameworkCoreModule>(); | ||||
| 
 | ||||
|             Configure<AbpAutoMapperOptions>(options => | ||||
|             { | ||||
|                 options.AddProfile<AbpAuditingMapperProfile>(validate: true); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| using AutoMapper; | ||||
| 
 | ||||
| namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore | ||||
| { | ||||
|     public class AbpAuditingMapperProfile : Profile | ||||
|     { | ||||
|         public AbpAuditingMapperProfile() | ||||
|         { | ||||
|             CreateMap<Volo.Abp.AuditLogging.AuditLogAction, AuditLogAction>() | ||||
|                 .MapExtraProperties(); | ||||
|             CreateMap<Volo.Abp.AuditLogging.EntityPropertyChange, EntityPropertyChange>(); | ||||
|             CreateMap<Volo.Abp.AuditLogging.EntityChange, EntityChange>() | ||||
|                 .MapExtraProperties(); | ||||
|             CreateMap<Volo.Abp.AuditLogging.AuditLog, AuditLog>() | ||||
|                 .MapExtraProperties(); | ||||
| 
 | ||||
|             CreateMap<Volo.Abp.Identity.IdentitySecurityLog, SecurityLog>() | ||||
|                 .MapExtraProperties(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLogManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public AuditLogManager( | ||||
|             IObjectMapper objectMapper, | ||||
|             IAuditLogRepository auditLogRepository, | ||||
|             IUnitOfWorkManager unitOfWorkManager, | ||||
|             IOptions<AbpAuditingOptions> options, | ||||
|             IAuditLogInfoToAuditLogConverter converter) | ||||
|         { | ||||
|             ObjectMapper = objectMapper; | ||||
|             AuditLogRepository = auditLogRepository; | ||||
|             UnitOfWorkManager = unitOfWorkManager; | ||||
|             Converter = converter; | ||||
|             Options = options.Value; | ||||
| 
 | ||||
|             Logger = NullLogger<AuditLogManager>.Instance; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public virtual async Task<long> GetCountAsync( | ||||
|             DateTime? startTime = null, | ||||
|             DateTime? endTime = null, | ||||
|             string httpMethod = null, | ||||
|             string url = null, | ||||
|             Guid? userId = null, | ||||
|             string userName = null, | ||||
|             string applicationName = null, | ||||
|             string 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<List<AuditLog>> 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<Volo.Abp.AuditLogging.AuditLog>, List<AuditLog>>(auditLogs); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<AuditLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             var auditLog = await AuditLogRepository.GetAsync(id, includeDetails, cancellationToken); | ||||
| 
 | ||||
|             return ObjectMapper.Map<Volo.Abp.AuditLogging.AuditLog, AuditLog>(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<string> 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<string> 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(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<SecurityLogManager> 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<SecurityLogManager> logger, | ||||
|             IOptions<AbpSecurityLogOptions> 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<SecurityLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             var securityLog = await IdentitySecurityLogRepository.GetAsync(id, includeDetails, cancellationToken); | ||||
| 
 | ||||
|             return ObjectMapper.Map<IdentitySecurityLog, SecurityLog>(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<List<SecurityLog>> 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<IdentitySecurityLog>, List<SecurityLog>>(securityLogs); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public virtual async Task<long> 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,17 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.ExceptionHandling" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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 | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChange> EntityChanges { get; set; } | ||||
| 
 | ||||
|         public List<AuditLogAction> Actions { get; set; } | ||||
| 
 | ||||
|         public ExtraPropertyDictionary ExtraProperties { get; set; } | ||||
| 
 | ||||
|         public AuditLog() | ||||
|         { | ||||
|             Actions = new List<AuditLogAction>(); | ||||
|             EntityChanges = new List<EntityChange>(); | ||||
|             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<EntityChange> entityChanges, | ||||
|             List<AuditLogAction> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<DefaultAuditLogManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public DefaultAuditLogManager() | ||||
|         { | ||||
|             Logger = NullLogger<DefaultAuditLogManager>.Instance; | ||||
|         } | ||||
| 
 | ||||
|         public virtual Task<long> GetCountAsync( | ||||
|             DateTime? startTime = null, | ||||
|             DateTime? endTime = null, | ||||
|             string httpMethod = null, | ||||
|             string url = null, | ||||
|             Guid? userId = null, | ||||
|             string userName = null, | ||||
|             string applicationName = null, | ||||
|             string 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<List<AuditLog>> 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<AuditLog>()); | ||||
|         } | ||||
| 
 | ||||
|         public virtual Task<string> 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<AuditLog> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChange> GetAsync(Guid entityChangeId, CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             EntityChange entityChange = null; | ||||
|             return Task.FromResult(entityChange); | ||||
|         } | ||||
| 
 | ||||
|         public Task<long> 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<List<EntityChange>> 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<EntityChange>()); | ||||
|         } | ||||
| 
 | ||||
|         public Task<EntityChangeWithUsername> GetWithUsernameAsync(Guid entityChangeId, CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             EntityChangeWithUsername entityChange = null; | ||||
|             return Task.FromResult(entityChange); | ||||
|         } | ||||
| 
 | ||||
|         public Task<List<EntityChangeWithUsername>> GetWithUsernameAsync(string entityId, string entityTypeFullName, CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             return Task.FromResult(new List<EntityChangeWithUsername>()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<DefaultSecurityLogManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public DefaultSecurityLogManager() | ||||
|         { | ||||
|             Logger = NullLogger<DefaultSecurityLogManager>.Instance; | ||||
|         } | ||||
| 
 | ||||
|         public Task<long> 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<List<SecurityLog>> 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<SecurityLog>()); | ||||
|         } | ||||
| 
 | ||||
|         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<SecurityLog> 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<EntityPropertyChange> PropertyChanges { get; set; } | ||||
| 
 | ||||
|         public ExtraPropertyDictionary ExtraProperties { get; set; } | ||||
| 
 | ||||
|         public EntityChange() | ||||
|         { | ||||
|             PropertyChanges = new List<EntityPropertyChange>(); | ||||
|             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<EntityPropertyChange>(); | ||||
| 
 | ||||
|             ExtraProperties = new ExtraPropertyDictionary(); | ||||
|             if (entityChangeInfo.ExtraProperties != null) | ||||
|             { | ||||
|                 foreach (var pair in entityChangeInfo.ExtraProperties) | ||||
|                 { | ||||
|                     ExtraProperties.Add(pair.Key, pair.Value); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| namespace Sanhe.Abp.AuditLogging | ||||
| { | ||||
|     public class EntityChangeWithUsername | ||||
|     { | ||||
|         public EntityChange EntityChange { get; set; } | ||||
| 
 | ||||
|         public string UserName { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task DeleteAsync( | ||||
|             Guid id, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<string> SaveAsync( | ||||
|             AuditLogInfo auditInfo, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<long> GetCountAsync( | ||||
|             DateTime? startTime = null, | ||||
|             DateTime? endTime = null, | ||||
|             string httpMethod = null, | ||||
|             string url = null, | ||||
|             Guid? userId = null, | ||||
|             string userName = null, | ||||
|             string applicationName = null, | ||||
|             string correlationId = null, | ||||
|             string clientId = null, | ||||
|             string clientIpAddress = null, | ||||
|             int? maxExecutionDuration = null, | ||||
|             int? minExecutionDuration = null, | ||||
|             bool? hasException = null, | ||||
|             HttpStatusCode? httpStatusCode = null, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<List<AuditLog>> 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); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChange> GetAsync( | ||||
|             Guid entityChangeId, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<long> GetCountAsync( | ||||
|             Guid? auditLogId = null, | ||||
|             DateTime? startTime = null, | ||||
|             DateTime? endTime = null, | ||||
|             EntityChangeType? changeType = null, | ||||
|             string entityId = null, | ||||
|             string entityTypeFullName = null, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<List<EntityChange>> 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<EntityChangeWithUsername> GetWithUsernameAsync( | ||||
|             Guid entityChangeId, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<List<EntityChangeWithUsername>> GetWithUsernameAsync( | ||||
|             string entityId, | ||||
|             string entityTypeFullName, | ||||
|             CancellationToken cancellationToken = default); | ||||
|     } | ||||
| } | ||||
| @ -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<SecurityLog> GetAsync( | ||||
|             Guid id, | ||||
|             bool includeDetails = false, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task DeleteAsync( | ||||
|             Guid id, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task SaveAsync( | ||||
|             SecurityLogInfo securityLogInfo, | ||||
|             CancellationToken cancellationToken = default); | ||||
| 
 | ||||
|         Task<List<SecurityLog>> 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<long> 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); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -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); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,28 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <None Remove="Sanhe\Abp\Auditing\Localization\Resources\en.json" /> | ||||
| 	  <None Remove="Sanhe\Abp\Auditing\Localization\Resources\zh-Hans.json" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 	  <EmbeddedResource Include="Sanhe\Abp\Auditing\Localization\Resources\en.json" /> | ||||
| 	  <EmbeddedResource Include="Sanhe\Abp\Auditing\Localization\Resources\zh-Hans.json" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Features" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.AuditLogging.Domain.Shared" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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<AbpVirtualFileSystemOptions>(options => | ||||
|             { | ||||
|                 options.FileSets.AddEmbedded<AbpAuditingApplicationContractsModule>(); | ||||
|             }); | ||||
| 
 | ||||
|             Configure<AbpLocalizationOptions>(options => | ||||
|             { | ||||
|                 options.Resources | ||||
|                     .Get<AuditLoggingResource>() | ||||
|                     .AddVirtualJson("/Sanhe/Abp/Auditing/Localization/Resources"); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,18 @@ | ||||
| using System; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Auditing.AuditLogs | ||||
| { | ||||
|     public class AuditLogActionDto : ExtensibleEntityDto<Guid> | ||||
|     { | ||||
|         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; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,55 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Auditing.AuditLogs | ||||
| { | ||||
|     public class AuditLogDto : ExtensibleEntityDto<Guid> | ||||
|     { | ||||
|         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<EntityChangeDto> EntityChanges { get; set; } | ||||
|         public List<AuditLogActionDto> Actions { get; set; } | ||||
| 
 | ||||
|         public AuditLogDto() | ||||
|         { | ||||
|             EntityChanges = new List<EntityChangeDto>(); | ||||
|             Actions = new List<AuditLogActionDto>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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<Guid> | ||||
|     { | ||||
|         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<EntityPropertyChangeDto> PropertyChanges { get; set; } | ||||
| 
 | ||||
|         public EntityChangeDto() | ||||
|         { | ||||
|             PropertyChanges = new List<EntityPropertyChangeDto>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| namespace Sanhe.Abp.Auditing.AuditLogs; | ||||
| 
 | ||||
| public class EntityChangeGetWithUsernameDto | ||||
| { | ||||
|     public string EntityId { get; set; } | ||||
|     public string EntityTypeFullName { get; set; } | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| namespace Sanhe.Abp.Auditing.AuditLogs; | ||||
| 
 | ||||
| public class EntityChangeWithUsernameDto | ||||
| { | ||||
|     public EntityChangeDto EntityChange { get; set; } | ||||
| 
 | ||||
|     public string UserName { get; set; } | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| using System; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Auditing.AuditLogs | ||||
| { | ||||
|     public class EntityPropertyChangeDto : EntityDto<Guid> | ||||
|     { | ||||
|         public string NewValue { get; set; } | ||||
| 
 | ||||
|         public string OriginalValue { get; set; } | ||||
| 
 | ||||
|         public string PropertyName { get; set; } | ||||
| 
 | ||||
|         public string PropertyTypeFullName { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -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<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogGetByPagedDto input); | ||||
| 
 | ||||
|         Task<AuditLogDto> GetAsync(Guid id); | ||||
| 
 | ||||
|         Task DeleteAsync(Guid id); | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChangeDto> GetAsync(Guid id); | ||||
| 
 | ||||
|     Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id); | ||||
| 
 | ||||
|     Task<PagedResultDto<EntityChangeDto>> GetListAsync(EntityChangeGetByPagedDto input); | ||||
| 
 | ||||
|     Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input); | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| namespace Sanhe.Abp.Auditing | ||||
| { | ||||
|     public static class AuditingRemoteServiceConsts | ||||
|     { | ||||
|         public const string RemoteServiceName = "AbpAuditing"; | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLoggingResource>(name); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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" | ||||
|   } | ||||
| } | ||||
| @ -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": "错误信息" | ||||
|   } | ||||
| } | ||||
| @ -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<LogExceptionDto> Exceptions { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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<LogDto> GetAsync(string id); | ||||
| 
 | ||||
|         Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input); | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLoggingResource>(name); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<PagedResultDto<SecurityLogDto>> GetListAsync(SecurityLogGetByPagedDto input); | ||||
| 
 | ||||
|         Task<SecurityLogDto> GetAsync(Guid id); | ||||
| 
 | ||||
|         Task DeleteAsync(Guid id); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| using System; | ||||
| using Volo.Abp.Application.Dtos; | ||||
| 
 | ||||
| namespace Sanhe.Abp.Auditing.SecurityLogs | ||||
| { | ||||
|     public class SecurityLogDto : ExtensibleEntityDto<Guid> | ||||
|     { | ||||
|         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; } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,22 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\logging\Sanhe.Abp.Logging\Sanhe.Abp.Logging.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.Auditing.Application.Contracts\Sanhe.Abp.Auditing.Application.Contracts.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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<AbpAuditingApplicationModule>(); | ||||
| 
 | ||||
|             Configure<AbpAutoMapperOptions>(options => | ||||
|             { | ||||
|                 options.AddProfile<AbpAuditingMapperProfile>(validate: true); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLogAction, AuditLogActionDto>() | ||||
|                 .MapExtraProperties(); | ||||
|             CreateMap<EntityPropertyChange, EntityPropertyChangeDto>(); | ||||
|             CreateMap<EntityChangeWithUsername, EntityChangeWithUsernameDto>(); | ||||
|             CreateMap<EntityChange, EntityChangeDto>() | ||||
|                 .MapExtraProperties(); | ||||
|             CreateMap<AuditLog, AuditLogDto>() | ||||
|                 .MapExtraProperties(); | ||||
| 
 | ||||
|             CreateMap<SecurityLog, SecurityLogDto>() | ||||
|                 .MapExtraProperties(); | ||||
| 
 | ||||
|             CreateMap<LogField, LogFieldDto>(); | ||||
|             CreateMap<LogException, LogExceptionDto>(); | ||||
|             CreateMap<LogInfo, LogDto>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AuditLogDto> GetAsync(Guid id) | ||||
|         { | ||||
|             var auditLog = await AuditLogManager.GetAsync(id, includeDetails: true); | ||||
| 
 | ||||
|             return ObjectMapper.Map<AuditLog, AuditLogDto>(auditLog); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<PagedResultDto<AuditLogDto>> 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<AuditLogDto>(auditLogCount, | ||||
|                 ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogs)); | ||||
|         } | ||||
| 
 | ||||
|         [Authorize(AuditingPermissionNames.AuditLog.Delete)] | ||||
|         public virtual async Task DeleteAsync([Required] Guid id) | ||||
|         { | ||||
|             await AuditLogManager.DeleteAsync(id); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChangeDto> GetAsync(Guid id) | ||||
|     { | ||||
|         var entityChange = await EntityChangeStore.GetAsync(id); | ||||
| 
 | ||||
|         return ObjectMapper.Map<EntityChange, EntityChangeDto>(entityChange); | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<PagedResultDto<EntityChangeDto>> 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<EntityChangeDto>(totalCount, | ||||
|             ObjectMapper.Map<List<EntityChange>, List<EntityChangeDto>>(entityChanges)); | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id) | ||||
|     { | ||||
|         var entityChangeWithUsername = await EntityChangeStore.GetWithUsernameAsync(id); | ||||
| 
 | ||||
|         return ObjectMapper.Map<EntityChangeWithUsername, EntityChangeWithUsernameDto>(entityChangeWithUsername); | ||||
|     } | ||||
| 
 | ||||
|     public async virtual Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) | ||||
|     { | ||||
|         var entityChangeWithUsernames = await EntityChangeStore.GetWithUsernameAsync( | ||||
|             input.EntityId, input.EntityTypeFullName); | ||||
| 
 | ||||
|         return new ListResultDto<EntityChangeWithUsernameDto>( | ||||
|             ObjectMapper.Map<List<EntityChangeWithUsername>, List<EntityChangeWithUsernameDto>>(entityChangeWithUsernames)); | ||||
|     } | ||||
| } | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<LogDto> GetAsync(string id) | ||||
|         { | ||||
|             var log = await _manager.GetAsync(id); | ||||
| 
 | ||||
|             return ObjectMapper.Map<LogInfo, LogDto>(log); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<PagedResultDto<LogDto>> 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<LogDto>(count, | ||||
|                 ObjectMapper.Map<List<LogInfo>, List<LogDto>>(logs)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<SecurityLogDto> GetAsync(Guid id) | ||||
|         { | ||||
|             var securityLog = await SecurityLogManager.GetAsync(id, includeDetails: true); | ||||
| 
 | ||||
|             return ObjectMapper.Map<SecurityLog, SecurityLogDto>(securityLog); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<PagedResultDto<SecurityLogDto>> 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<SecurityLogDto>(securityLogCount, | ||||
|                 ObjectMapper.Map<List<SecurityLog>, List<SecurityLogDto>>(securityLogs)); | ||||
|         } | ||||
| 
 | ||||
|         [Authorize(AuditingPermissionNames.SecurityLog.Delete)] | ||||
|         public virtual async Task DeleteAsync(Guid id) | ||||
|         { | ||||
|             await SecurityLogManager.DeleteAsync(id); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,19 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>net6.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.Auditing.Application.Contracts\Sanhe.Abp.Auditing.Application.Contracts.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -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<IMvcBuilder>(mvcBuilder => | ||||
|             { | ||||
|                 mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAuditingHttpApiModule).Assembly); | ||||
|             }); | ||||
| 
 | ||||
|             PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => | ||||
|             { | ||||
|                 options.AddAssemblyResource(typeof(AuditLoggingResource), typeof(AbpAuditingApplicationContractsModule).Assembly); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         public override void ConfigureServices(ServiceConfigurationContext context) | ||||
|         { | ||||
|             Configure<AbpLocalizationOptions>(options => | ||||
|             { | ||||
|                 options.Resources | ||||
|                     .Get<AuditLoggingResource>() | ||||
|                     .AddBaseTypes( | ||||
|                         typeof(AbpUiResource) | ||||
|                     ); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 审计日志 | ||||
|     /// </summary> | ||||
|     [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; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 根据Id获取 | ||||
|         /// </summary> | ||||
|         /// <param name="id"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         [Route("{id}")] | ||||
|         public virtual Task<AuditLogDto> GetAsync(Guid id) | ||||
|         { | ||||
|             return AuditLogAppService.GetAsync(id); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 分页获取 | ||||
|         /// </summary> | ||||
|         /// <param name="input"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         public virtual Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogGetByPagedDto input) | ||||
|         { | ||||
|             return AuditLogAppService.GetListAsync(input); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 根据Id删除 | ||||
|         /// </summary> | ||||
|         /// <param name="id"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpDelete] | ||||
|         [Route("{id}")] | ||||
|         public virtual Task DeleteAsync(Guid id) | ||||
|         { | ||||
|             return AuditLogAppService.DeleteAsync(id); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<EntityChangeDto> GetAsync(Guid id) | ||||
|     { | ||||
|         return EntityChangeAppService.GetAsync(id); | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     public Task<PagedResultDto<EntityChangeDto>> GetListAsync(EntityChangeGetByPagedDto input) | ||||
|     { | ||||
|         return EntityChangeAppService.GetListAsync(input); | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     [Route("with-username/{id}")] | ||||
|     public Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id) | ||||
|     { | ||||
|         return EntityChangeAppService.GetWithUsernameAsync(id); | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     [Route("with-username")] | ||||
|     public Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) | ||||
|     { | ||||
|         return EntityChangeAppService.GetWithUsernameAsync(input); | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志 | ||||
|     /// </summary> | ||||
|     [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; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 根据Id获取 | ||||
|         /// </summary> | ||||
|         /// <param name="id"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         [Route("{id}")] | ||||
|         public virtual Task<LogDto> GetAsync(string id) | ||||
|         { | ||||
|             return _service.GetAsync(id); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 分页获取 | ||||
|         /// </summary> | ||||
|         /// <param name="input"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         public virtual Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input) | ||||
|         { | ||||
|             return _service.GetListAsync(input); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 安全日志 | ||||
|     /// </summary> | ||||
|     [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; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 根据Id获取 | ||||
|         /// </summary> | ||||
|         /// <param name="id"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         [Route("{id}")] | ||||
|         public virtual Task<SecurityLogDto> GetAsync(Guid id) | ||||
|         { | ||||
|             return SecurityLogAppService.GetAsync(id); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 分页获取 | ||||
|         /// </summary> | ||||
|         /// <param name="input"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpGet] | ||||
|         public virtual Task<PagedResultDto<SecurityLogDto>> GetListAsync(SecurityLogGetByPagedDto input) | ||||
|         { | ||||
|             return SecurityLogAppService.GetListAsync(input); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 根据Id删除 | ||||
|         /// </summary> | ||||
|         /// <param name="id"></param> | ||||
|         /// <returns></returns> | ||||
|         [HttpDelete] | ||||
|         [Route("{id}")] | ||||
|         public virtual Task DeleteAsync(Guid id) | ||||
|         { | ||||
|             return SecurityLogAppService.DeleteAsync(id); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -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}" | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ``` | ||||
| @ -0,0 +1,24 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="$(SerilogSinksElasticsearchPackageVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> | ||||
| 		<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\elasticsearch\Sanhe.Abp.Elasticsearch\Sanhe.Abp.Elasticsearch.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.Logging\Sanhe.Abp.Logging.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.Serilog.Enrichers.Application\Sanhe.Abp.Serilog.Enrichers.Application.csproj" /> | ||||
| 		<ProjectReference Include="..\Sanhe.Abp.Serilog.Enrichers.UniqueId\Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
| @ -0,0 +1,16 @@ | ||||
| using AutoMapper; | ||||
| using Sanhe.Abp.Logging; | ||||
| 
 | ||||
| namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch | ||||
| { | ||||
|     public class AbpLoggingSerilogElasticsearchMapperProfile : Profile | ||||
|     { | ||||
|         public AbpLoggingSerilogElasticsearchMapperProfile() | ||||
|         { | ||||
|             CreateMap<SerilogException, LogException>(); | ||||
|             CreateMap<SerilogField, LogField>() | ||||
|                 .ForMember(log => log.Id, map => map.MapFrom(slog => slog.UniqueId.ToString())); | ||||
|             CreateMap<SerilogInfo, LogInfo>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<AbpLoggingSerilogElasticsearchOptions>(configuration.GetSection("Logging:Serilog:Elasticsearch")); | ||||
| 
 | ||||
|             context.Services.AddAutoMapperObjectMapper<AbpLoggingSerilogElasticsearchModule>(); | ||||
| 
 | ||||
|             Configure<AbpAutoMapperOptions>(options => | ||||
|             { | ||||
|                 options.AddProfile<AbpLoggingSerilogElasticsearchMapperProfile>(validate: true); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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}"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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<SerilogElasticsearchLoggingManager> Logger { protected get; set; } | ||||
| 
 | ||||
|         public SerilogElasticsearchLoggingManager( | ||||
|             IObjectMapper objectMapper, | ||||
|             ICurrentTenant currentTenant, | ||||
|             IOptions<AbpLoggingSerilogElasticsearchOptions> options, | ||||
|             IElasticsearchClientFactory clientFactory) | ||||
|         { | ||||
|             _objectMapper = objectMapper; | ||||
|             _currentTenant = currentTenant; | ||||
|             _clientFactory = clientFactory; | ||||
|             _options = options.Value; | ||||
| 
 | ||||
|             Logger = NullLogger<SerilogElasticsearchLoggingManager>.Instance; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         ///  | ||||
|         /// </summary> | ||||
|         /// <param name="id">时间类型或者转换为timestamp都可以查询</param> | ||||
|         /// <param name="cancellationToken"></param> | ||||
|         /// <returns></returns> | ||||
|         public virtual async Task<LogInfo> GetAsync( | ||||
|             string id, | ||||
|             CancellationToken cancellationToken = default) | ||||
|         { | ||||
|             var client = _clientFactory.Create(); | ||||
| 
 | ||||
|             ISearchResponse<SerilogInfo> response; | ||||
| 
 | ||||
|             if (_currentTenant.IsAvailable) | ||||
|             { | ||||
|                 /* | ||||
|                 "query": { | ||||
|                     "bool": { | ||||
|                         "must": [ | ||||
|                             { | ||||
|                                 "term": { | ||||
|                                     "fields.TenantId.keyword": { | ||||
|                                         "value": _currentTenant.GetId() | ||||
|                                     } | ||||
|                                 } | ||||
|                             }, | ||||
|                             { | ||||
|                                 "term": { | ||||
|                                     "fields.UniqueId": { | ||||
|                                         "value": "1474021081433481216" | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         ] | ||||
|                     } | ||||
|                 } | ||||
|                 */ | ||||
|                 response = await client.SearchAsync<SerilogInfo>( | ||||
|                     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<SerilogInfo>( | ||||
|                     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<SerilogInfo, LogInfo>(response.Documents.FirstOrDefault()); | ||||
|         } | ||||
| 
 | ||||
|         public virtual async Task<long> 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<SerilogInfo>((dsl) => | ||||
|                 dsl.Index(CreateIndex()) | ||||
|                    .Query(log => log.Bool(b => b.Must(querys.ToArray()))), | ||||
|                 cancellationToken); | ||||
| 
 | ||||
|             return response.Count; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 获取日志列表 | ||||
|         /// </summary> | ||||
|         /// <param name="sorting">排序字段</param> | ||||
|         /// <param name="maxResultCount"></param> | ||||
|         /// <param name="skipCount"></param> | ||||
|         /// <param name="startTime"></param> | ||||
|         /// <param name="endTime"></param> | ||||
|         /// <param name="level"></param> | ||||
|         /// <param name="machineName"></param> | ||||
|         /// <param name="environment"></param> | ||||
|         /// <param name="application"></param> | ||||
|         /// <param name="context"></param> | ||||
|         /// <param name="requestId"></param> | ||||
|         /// <param name="requestPath"></param> | ||||
|         /// <param name="correlationId"></param> | ||||
|         /// <param name="processId"></param> | ||||
|         /// <param name="threadId"></param> | ||||
|         /// <param name="hasException"></param> | ||||
|         /// <param name="includeDetails"></param> | ||||
|         /// <param name="cancellationToken"></param> | ||||
|         /// <returns></returns> | ||||
|         public virtual async Task<List<LogInfo>> 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<SerilogInfo> SourceFilter(SourceFilterDescriptor<SerilogInfo> selector) | ||||
|             { | ||||
|                 selector.IncludeAll(); | ||||
|                 if (!includeDetails) | ||||
|                 { | ||||
|                     selector.Excludes(field => | ||||
|                         field.Field("exceptions")); | ||||
|                 } | ||||
| 
 | ||||
|                 return selector; | ||||
|             } | ||||
| 
 | ||||
|             var response = await client.SearchAsync<SerilogInfo>((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<SerilogInfo>, List<LogInfo>>(response.Documents.ToList()); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual List<Func<QueryContainerDescriptor<SerilogInfo>, 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<Func<QueryContainerDescriptor<SerilogInfo>, 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<string, string> _fieldMaps = new Dictionary<string, string>(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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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; } | ||||
|     } | ||||
| } | ||||
| @ -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<SerilogException> Exceptions { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <ConfigureAwait ContinueOnCapturedContext="false" /> | ||||
| </Weavers> | ||||
| @ -0,0 +1,15 @@ | ||||
| # LINGYUN.Abp.Logging | ||||
| 
 | ||||
| 日志基础模块 | ||||
| 
 | ||||
| 定义 ILoggingManager 接口, 实现日志信息查询   | ||||
| 
 | ||||
| ## 模块引用 | ||||
| 
 | ||||
| ```csharp | ||||
| [DependsOn(typeof(AbpLoggingModule))] | ||||
| public class YouProjectModule : AbpModule | ||||
| { | ||||
|   // other | ||||
| } | ||||
| ``` | ||||
| @ -0,0 +1,14 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 
 | ||||
| 	<Import Project="..\..\..\configureawait.props" /> | ||||
| 	<Import Project="..\..\..\common.props" /> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFramework>netstandard2.0</TargetFramework> | ||||
| 		<RootNamespace /> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Volo.Abp.Core" Version="$(VoloAbpVersion)" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @ -0,0 +1,8 @@ | ||||
| namespace Sanhe.Abp.Logging | ||||
| { | ||||
|     public class AbpLoggingEnricherPropertyNames | ||||
|     { | ||||
|         public const string MachineName = "MachineName"; | ||||
|         public const string EnvironmentName = "EnvironmentName"; | ||||
|     } | ||||
| } | ||||
| @ -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<AbpLoggingEnricherPropertyNames>(configuration.GetSection("Logging")); | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue