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