81 changed files with 3500 additions and 449 deletions
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Json" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\elasticsearch\LINGYUN.Abp.Elasticsearch\LINGYUN.Abp.Elasticsearch.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AuditLogging\LINGYUN.Abp.AuditLogging.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.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")); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
public class AbpAuditLoggingElasticsearchOptions |
|||
{ |
|||
public const string DefaultIndexPrefix = "auditlogging"; |
|||
public string IndexPrefix { get; set; } |
|||
|
|||
public AbpAuditLoggingElasticsearchOptions() |
|||
{ |
|||
IndexPrefix = DefaultIndexPrefix; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
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 LINGYUN.Abp.AuditLogging |
|||
{ |
|||
public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency |
|||
{ |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
|
|||
public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator, IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer) |
|||
{ |
|||
GuidGenerator = guidGenerator; |
|||
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, true)) |
|||
?? 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,347 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
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.Net; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
public class ElasticsearchAuditLogManager : IAuditLogManager, ISingletonDependency |
|||
{ |
|||
private readonly AbpAuditingOptions _auditingOptions; |
|||
private readonly AbpElasticsearchOptions _elasticsearchOptions; |
|||
private readonly AbpAuditLoggingElasticsearchOptions _options; |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IElasticsearchClientFactory _clientFactory; |
|||
private readonly IAuditLogInfoToAuditLogConverter _converter; |
|||
|
|||
public ILogger<ElasticsearchAuditLogManager> Logger { protected get; set; } |
|||
|
|||
public ElasticsearchAuditLogManager( |
|||
ICurrentTenant currentTenant, |
|||
IOptions<AbpElasticsearchOptions> elasticsearchOptions, |
|||
IOptions<AbpAuditLoggingElasticsearchOptions> options, |
|||
IElasticsearchClientFactory clientFactory, |
|||
IOptions<AbpAuditingOptions> auditingOptions, |
|||
IAuditLogInfoToAuditLogConverter converter) |
|||
{ |
|||
_options = options.Value; |
|||
_converter = converter; |
|||
_clientFactory = clientFactory; |
|||
_currentTenant = currentTenant; |
|||
_auditingOptions = auditingOptions.Value; |
|||
_elasticsearchOptions = elasticsearchOptions.Value; |
|||
|
|||
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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
var client = _clientFactory.Create(); |
|||
|
|||
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("desc", StringComparison.InvariantCultureIgnoreCase) |
|||
? SortOrder.Descending : SortOrder.Ascending; |
|||
sorting = !sorting.IsNullOrWhiteSpace() |
|||
? sorting.Split()[0] |
|||
: nameof(AuditLog.ExecutionTime); |
|||
|
|||
if (_elasticsearchOptions.FieldCamelCase) |
|||
{ |
|||
sorting = sorting.ToCamelCase(); |
|||
} |
|||
|
|||
var querys = BuildQueryDescriptor( |
|||
startTime, |
|||
endTime, |
|||
httpMethod, |
|||
url, |
|||
userId, |
|||
userName, |
|||
applicationName, |
|||
correlationId, |
|||
clientId, |
|||
clientIpAddress, |
|||
maxExecutionDuration, |
|||
minExecutionDuration, |
|||
hasException, |
|||
httpStatusCode); |
|||
|
|||
SourceFilterDescriptor<AuditLog> ConvertFileSystem(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(ConvertFileSystem) |
|||
.Sort(log => log.Field(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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
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(f => f.ExecutionTime).GreaterThanOrEquals(startTime))); |
|||
} |
|||
if (endTime.HasValue) |
|||
{ |
|||
querys.Add((log) => log.DateRange((q) => q.Field(f => f.ExecutionTime).LessThanOrEquals(endTime))); |
|||
} |
|||
if (!httpMethod.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.HttpMethod.Suffix("keyword")).Value(httpMethod))); |
|||
} |
|||
if (!url.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Match((q) => q.Field(f => f.Url.Suffix("keyword")).Query(url))); |
|||
} |
|||
if (userId.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.UserId.Suffix("keyword")).Value(userId))); |
|||
} |
|||
if (!userName.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.UserName.Suffix("keyword")).Value(userName))); |
|||
} |
|||
if (!applicationName.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ApplicationName.Suffix("keyword")).Value(applicationName))); |
|||
} |
|||
if (!correlationId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.CorrelationId.Suffix("keyword")).Value(correlationId))); |
|||
} |
|||
if (!clientId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ClientId.Suffix("keyword")).Value(clientId))); |
|||
} |
|||
if (!clientIpAddress.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ClientIpAddress.Suffix("keyword")).Value(clientIpAddress))); |
|||
} |
|||
if (maxExecutionDuration.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Range((q) => q.Field(f => f.ExecutionDuration).LessThanOrEquals(maxExecutionDuration))); |
|||
} |
|||
if (minExecutionDuration.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Range((q) => q.Field(f => f.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(f => f.HttpStatusCode).Value(httpStatusCode))); |
|||
} |
|||
|
|||
return querys; |
|||
} |
|||
|
|||
protected virtual string CreateIndex() |
|||
{ |
|||
if (_currentTenant.IsAvailable) |
|||
{ |
|||
return $"{_options.IndexPrefix}-audit-log-{_currentTenant.Id:N}"; |
|||
} |
|||
return _options.IndexPrefix.IsNullOrWhiteSpace() |
|||
? "audit-log" |
|||
: $"{_options.IndexPrefix}-audit-log"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,249 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
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.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.SecurityLog; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
public class ElasticsearchSecurityLogManager : ISecurityLogManager, ISingletonDependency |
|||
{ |
|||
private readonly AbpElasticsearchOptions _elasticsearchOptions; |
|||
private readonly AbpAuditLoggingElasticsearchOptions _options; |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
private readonly IElasticsearchClientFactory _clientFactory; |
|||
|
|||
public ILogger<ElasticsearchSecurityLogManager> Logger { protected get; set; } |
|||
|
|||
public ElasticsearchSecurityLogManager( |
|||
ICurrentTenant currentTenant, |
|||
IGuidGenerator guidGenerator, |
|||
IOptions<AbpElasticsearchOptions> elasticsearchOptions, |
|||
IOptions<AbpAuditLoggingElasticsearchOptions> options, |
|||
IElasticsearchClientFactory clientFactory) |
|||
{ |
|||
_options = options.Value; |
|||
_currentTenant = currentTenant; |
|||
_guidGenerator = guidGenerator; |
|||
_clientFactory = clientFactory; |
|||
_elasticsearchOptions = elasticsearchOptions.Value; |
|||
|
|||
Logger = NullLogger<ElasticsearchSecurityLogManager>.Instance; |
|||
} |
|||
|
|||
public virtual async Task SaveAsync( |
|||
SecurityLogInfo securityLogInfo, |
|||
CancellationToken cancellationToken = default(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
var client = _clientFactory.Create(); |
|||
|
|||
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("desc", StringComparison.InvariantCultureIgnoreCase) |
|||
? SortOrder.Descending : SortOrder.Ascending; |
|||
sorting = !sorting.IsNullOrWhiteSpace() |
|||
? sorting.Split()[0] |
|||
: nameof(SecurityLog.CreationTime); |
|||
|
|||
if (_elasticsearchOptions.FieldCamelCase) |
|||
{ |
|||
sorting = sorting.ToCamelCase(); |
|||
} |
|||
|
|||
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(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(CancellationToken)) |
|||
{ |
|||
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(f => f.CreationTime).GreaterThanOrEquals(startTime))); |
|||
} |
|||
if (endTime.HasValue) |
|||
{ |
|||
querys.Add((log) => log.DateRange((q) => q.Field(f => f.CreationTime).LessThanOrEquals(endTime))); |
|||
} |
|||
if (!applicationName.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ApplicationName.Suffix("keyword")).Value(applicationName))); |
|||
} |
|||
if (!identity.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.Identity.Suffix("keyword")).Value(identity))); |
|||
} |
|||
if (!action.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.Action.Suffix("keyword")).Value(action))); |
|||
} |
|||
if (userId.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.UserId.Suffix("keyword")).Value(userId))); |
|||
} |
|||
if (!userName.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.UserName.Suffix("keyword")).Value(userName))); |
|||
} |
|||
if (!clientId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ClientId.Suffix("keyword")).Value(clientId))); |
|||
} |
|||
if (!clientIpAddress.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.ClientIpAddress.Suffix("keyword")).Value(clientIpAddress))); |
|||
} |
|||
if (!correlationId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.CorrelationId.Suffix("keyword")).Value(correlationId))); |
|||
} |
|||
|
|||
return querys; |
|||
} |
|||
|
|||
protected virtual string CreateIndex() |
|||
{ |
|||
// TODO: 会出现索引很长的情况...
|
|||
if (_currentTenant.IsAvailable) |
|||
{ |
|||
return $"{_options.IndexPrefix}-security-log-{_currentTenant.Id:N}"; |
|||
} |
|||
return _options.IndexPrefix.IsNullOrWhiteSpace() |
|||
? "security-log" |
|||
: $"{_options.IndexPrefix}-security-log"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging |
|||
{ |
|||
public interface IAuditLogInfoToAuditLogConverter |
|||
{ |
|||
Task<AuditLog> ConvertAsync(AuditLogInfo auditLogInfo); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AuditLogging\LINGYUN.Abp.AuditLogging.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,25 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.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 LINGYUN.Abp.AuditLogging.EntityFrameworkCore |
|||
{ |
|||
public class AbpAuditingMapperProfile : Profile |
|||
{ |
|||
public AbpAuditingMapperProfile() |
|||
{ |
|||
CreateMap<Volo.Abp.AuditLogging.AuditLogAction, LINGYUN.Abp.AuditLogging.AuditLogAction>() |
|||
.MapExtraProperties(); |
|||
CreateMap<Volo.Abp.AuditLogging.EntityPropertyChange, LINGYUN.Abp.AuditLogging.EntityPropertyChange>(); |
|||
CreateMap<Volo.Abp.AuditLogging.EntityChange, LINGYUN.Abp.AuditLogging.EntityChange>() |
|||
.MapExtraProperties(); |
|||
CreateMap<Volo.Abp.AuditLogging.AuditLog, LINGYUN.Abp.AuditLogging.AuditLog>() |
|||
.MapExtraProperties(); |
|||
|
|||
CreateMap<Volo.Abp.Identity.IdentitySecurityLog, LINGYUN.Abp.AuditLogging.SecurityLog>() |
|||
.MapExtraProperties(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,177 @@ |
|||
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 LINGYUN.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(CancellationToken)) |
|||
{ |
|||
return await AuditLogRepository.GetCountAsync( |
|||
startTime, |
|||
endTime, |
|||
httpMethod, |
|||
url, |
|||
userId, |
|||
userName, |
|||
applicationName, |
|||
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(CancellationToken)) |
|||
{ |
|||
var auditLogs = await AuditLogRepository.GetListAsync( |
|||
sorting, |
|||
maxResultCount, |
|||
skipCount, |
|||
startTime, |
|||
endTime, |
|||
httpMethod, |
|||
url, |
|||
userId, |
|||
userName, |
|||
applicationName, |
|||
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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
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 LINGYUN.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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
return await IdentitySecurityLogRepository.GetCountAsync( |
|||
startTime, |
|||
endTime, |
|||
applicationName, |
|||
identity, |
|||
action, |
|||
userId, |
|||
userName, |
|||
clientId, |
|||
correlationId, |
|||
cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Auditing" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.Guids" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.ExceptionHandling" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.ExceptionHandling; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.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 LINGYUN.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 LINGYUN.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 LINGYUN.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 LINGYUN.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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
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,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 LINGYUN.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(CancellationToken)) |
|||
{ |
|||
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(CancellationToken)) |
|||
{ |
|||
Logger.LogDebug("No security log manager is available!"); |
|||
return Task.FromResult(new List<SecurityLog>()); |
|||
} |
|||
|
|||
public Task SaveAsync( |
|||
SecurityLogInfo securityLogInfo, |
|||
CancellationToken cancellationToken = default(CancellationToken)) |
|||
{ |
|||
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 LINGYUN.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,43 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace LINGYUN.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 LINGYUN.Abp.AuditLogging |
|||
{ |
|||
public interface IAuditLogManager |
|||
{ |
|||
Task<AuditLog> GetAsync( |
|||
Guid id, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
Task DeleteAsync( |
|||
Guid id, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
Task<string> SaveAsync( |
|||
AuditLogInfo auditInfo, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
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(CancellationToken)); |
|||
|
|||
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(CancellationToken)); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.SecurityLog; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging |
|||
{ |
|||
public interface ISecurityLogManager |
|||
{ |
|||
Task<SecurityLog> GetAsync( |
|||
Guid id, |
|||
bool includeDetails = false, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
Task DeleteAsync( |
|||
Guid id, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
Task SaveAsync( |
|||
SecurityLogInfo securityLogInfo, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
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(CancellationToken)); |
|||
|
|||
|
|||
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(CancellationToken)); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.SecurityLog; |
|||
|
|||
namespace LINGYUN.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 LINGYUN.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); |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
namespace LINGYUN.Abp.Auditing.AuditLogs |
|||
{ |
|||
public class AuditLogActionDto : ExtensibleEntityDto<Guid> |
|||
{ |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
namespace LINGYUN.Abp.Auditing.AuditLogs |
|||
{ |
|||
public class EntityPropertyChangeDto : EntityDto<Guid> |
|||
{ |
|||
@ -0,0 +1,15 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
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 LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
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,17 @@ |
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
public class LogFieldDto |
|||
{ |
|||
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,18 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
public class LogGetByPagedDto : PagedAndSortedResultRequestDto |
|||
{ |
|||
public DateTime? StartTime { get; set; } |
|||
public DateTime? EndTime { 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 System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
public interface ILogAppService : IApplicationService |
|||
{ |
|||
Task<LogDto> GetAsync(string id); |
|||
|
|||
Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input); |
|||
} |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Security |
|||
namespace LINGYUN.Abp.Auditing.SecurityLogs |
|||
{ |
|||
public class SecurityLogDto : ExtensibleEntityDto<Guid> |
|||
{ |
|||
@ -1,7 +1,7 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Security |
|||
namespace LINGYUN.Abp.Auditing.SecurityLogs |
|||
{ |
|||
public class SecurityLogGetByPagedDto : PagedAndSortedResultRequestDto |
|||
{ |
|||
@ -0,0 +1,44 @@ |
|||
using LINGYUN.Abp.Logging; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace LINGYUN.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.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.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,35 @@ |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.Auditing.Logging |
|||
{ |
|||
[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] |
|||
[Area("auditing")] |
|||
[ControllerName("logging")] |
|||
[Route("api/auditing/logging")] |
|||
public class LogController : AbpController, ILogAppService |
|||
{ |
|||
private readonly ILogAppService _service; |
|||
|
|||
public LogController(ILogAppService service) |
|||
{ |
|||
_service = service; |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("{id}")] |
|||
public virtual async Task<LogDto> GetAsync(string id) |
|||
{ |
|||
return await _service.GetAsync(id); |
|||
} |
|||
|
|||
[HttpGet] |
|||
public virtual async Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input) |
|||
{ |
|||
return await _service.GetListAsync(input); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="NEST" Version="7.15.1" /> |
|||
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Elasticsearch |
|||
{ |
|||
public class AbpElasticsearchModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpElasticsearchOptions>(configuration.GetSection("Elasticsearch")); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using Elasticsearch.Net; |
|||
using Nest; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace LINGYUN.Abp.Elasticsearch |
|||
{ |
|||
public class AbpElasticsearchOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 字段名称 是否为 camelCase 格式
|
|||
/// 如果为true, 在ES中为 camelCase
|
|||
/// 如果为false,在ES中为 CamelCase
|
|||
/// 默认:false
|
|||
/// </summary>
|
|||
public bool FieldCamelCase { get; set; } |
|||
public string TypeName { get; set; } |
|||
public string NodeUris { get; set; } |
|||
public int ConnectionLimit { get; set; } |
|||
public string UserName { get; set; } |
|||
public string Password { get; set; } |
|||
public TimeSpan ConnectionTimeout { get; set; } |
|||
public IConnection Connection { get; set; } |
|||
public ConnectionSettings.SourceSerializerFactory SerializerFactory { get; set; } |
|||
|
|||
public AbpElasticsearchOptions() |
|||
{ |
|||
TypeName = "_doc"; |
|||
ConnectionLimit = ConnectionConfiguration.DefaultConnectionLimit; |
|||
ConnectionTimeout = ConnectionConfiguration.DefaultTimeout; |
|||
} |
|||
|
|||
internal IConnectionSettingsValues CreateConfiguration() |
|||
{ |
|||
IConnectionPool connectionPool; |
|||
IEnumerable<Uri> nodes = NodeUris |
|||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) |
|||
.Select(uriString => new Uri(uriString)); |
|||
if (nodes.Count() == 1) |
|||
{ |
|||
connectionPool = new SingleNodeConnectionPool(nodes.First()); |
|||
} |
|||
else |
|||
{ |
|||
connectionPool = new StaticConnectionPool(nodes); |
|||
} |
|||
|
|||
var configuration = new ConnectionSettings( |
|||
connectionPool, |
|||
Connection, |
|||
SerializerFactory) |
|||
.ConnectionLimit(ConnectionLimit) |
|||
.RequestTimeout(ConnectionTimeout); |
|||
|
|||
if (!FieldCamelCase) |
|||
{ |
|||
configuration.DefaultFieldNameInferrer((name) => name); |
|||
} |
|||
|
|||
if (UserName.IsNullOrWhiteSpace()) |
|||
{ |
|||
configuration.BasicAuthentication(UserName, Password); |
|||
} |
|||
|
|||
return configuration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Nest; |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Elasticsearch |
|||
{ |
|||
public class ElasticsearchClientFactory : IElasticsearchClientFactory, ISingletonDependency |
|||
{ |
|||
private readonly AbpElasticsearchOptions _options; |
|||
private readonly Lazy<IElasticClient> _lazyClient; |
|||
|
|||
public ElasticsearchClientFactory( |
|||
IOptions<AbpElasticsearchOptions> options) |
|||
{ |
|||
_options = options.Value; |
|||
|
|||
_lazyClient = new Lazy<IElasticClient>(CreateClient); |
|||
} |
|||
|
|||
public IElasticClient Create() => _lazyClient.Value; |
|||
|
|||
protected virtual IElasticClient CreateClient() |
|||
{ |
|||
var configuration = _options.CreateConfiguration(); |
|||
|
|||
var client = new ElasticClient(configuration); |
|||
|
|||
return client; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Nest; |
|||
|
|||
namespace LINGYUN.Abp.Elasticsearch |
|||
{ |
|||
public interface IElasticsearchClientFactory |
|||
{ |
|||
IElasticClient Create(); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" /> |
|||
<PackageReference Include="Volo.Abp.AutoMapper" Version="4.4.0" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\elasticsearch\LINGYUN.Abp.Elasticsearch\LINGYUN.Abp.Elasticsearch.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Logging\LINGYUN.Abp.Logging.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace LINGYUN.Abp.Logging.Serilog.Elasticsearch |
|||
{ |
|||
public class AbpLoggingSerilogElasticsearchMapperProfile : Profile |
|||
{ |
|||
public AbpLoggingSerilogElasticsearchMapperProfile() |
|||
{ |
|||
CreateMap<SerilogException, LogException>(); |
|||
CreateMap<SerilogField, LogField>(); |
|||
CreateMap<SerilogInfo, LogInfo>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Json.SystemTextJson; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Logging.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 LINGYUN.Abp.Logging.Serilog.Elasticsearch |
|||
{ |
|||
public class AbpLoggingSerilogElasticsearchOptions |
|||
{ |
|||
public string IndexFormat { get; set; } |
|||
|
|||
public AbpLoggingSerilogElasticsearchOptions() |
|||
{ |
|||
IndexFormat = "logstash-{0:yyyy.MM.dd}"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,265 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
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.ObjectMapping; |
|||
|
|||
namespace LINGYUN.Abp.Logging.Serilog.Elasticsearch |
|||
{ |
|||
[Dependency(ReplaceServices = true)] |
|||
public class SerilogElasticsearchLoggingManager : ILoggingManager, ISingletonDependency |
|||
{ |
|||
private static readonly Regex IndexFormatRegex = new Regex(@"^(.*)(?:\{0\:.+\})(.*)$"); |
|||
|
|||
private readonly IObjectMapper _objectMapper; |
|||
private readonly AbpLoggingSerilogElasticsearchOptions _options; |
|||
private readonly IElasticsearchClientFactory _clientFactory; |
|||
|
|||
public ILogger<SerilogElasticsearchLoggingManager> Logger { protected get; set; } |
|||
|
|||
public SerilogElasticsearchLoggingManager( |
|||
IObjectMapper objectMapper, |
|||
IOptions<AbpLoggingSerilogElasticsearchOptions> options, |
|||
IElasticsearchClientFactory clientFactory) |
|||
{ |
|||
_objectMapper = objectMapper; |
|||
_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(CancellationToken)) |
|||
{ |
|||
var client = _clientFactory.Create(); |
|||
|
|||
var response = await client.SearchAsync<SerilogInfo>( |
|||
dsl => |
|||
dsl.Index(CreateIndex()) |
|||
.Query( |
|||
(q) => q.Bool( |
|||
(b) => b.Should( |
|||
(s) => s.Term( |
|||
(t) => t.Field("@timestamp").Value(id))))), |
|||
cancellationToken); |
|||
|
|||
return _objectMapper.Map<SerilogInfo, LogInfo>(response.Documents.FirstOrDefault()); |
|||
} |
|||
|
|||
public virtual async Task<long> GetCountAsync( |
|||
DateTime? startTime = null, |
|||
DateTime? endTime = 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(CancellationToken)) |
|||
{ |
|||
var client = _clientFactory.Create(); |
|||
|
|||
var querys = BuildQueryDescriptor( |
|||
startTime, |
|||
endTime, |
|||
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">排序字段,注意:忽略排序字段仅使用timestamp排序,根据传递的ASC、DESC字段区分倒序还是正序</param>
|
|||
/// <param name="maxResultCount"></param>
|
|||
/// <param name="skipCount"></param>
|
|||
/// <param name="startTime"></param>
|
|||
/// <param name="endTime"></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, |
|||
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(CancellationToken)) |
|||
{ |
|||
var client = _clientFactory.Create(); |
|||
|
|||
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("desc", StringComparison.InvariantCultureIgnoreCase) |
|||
? SortOrder.Descending : SortOrder.Ascending; |
|||
|
|||
var querys = BuildQueryDescriptor( |
|||
startTime, |
|||
endTime, |
|||
context, |
|||
requestId, |
|||
requestPath, |
|||
correlationId, |
|||
processId, |
|||
threadId, |
|||
hasException); |
|||
|
|||
SourceFilterDescriptor<SerilogInfo> ConvertFileSystem(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(ConvertFileSystem) |
|||
.Sort(log => log.Field("@timestamp", 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, |
|||
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 (startTime.HasValue) |
|||
{ |
|||
querys.Add((log) => log.DateRange((q) => q.Field(f => f.TimeStamp).GreaterThanOrEquals(startTime))); |
|||
} |
|||
if (endTime.HasValue) |
|||
{ |
|||
querys.Add((log) => log.DateRange((q) => q.Field(f => f.TimeStamp).LessThanOrEquals(endTime))); |
|||
} |
|||
if (!context.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field((f) => f.Fields.Context.Suffix("keyword")).Value(context))); |
|||
} |
|||
if (!requestId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Match((q) => q.Field(f => f.Fields.RequestId.Suffix("keyword")).Query(requestId))); |
|||
} |
|||
if (!requestPath.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.Fields.RequestPath.Suffix("keyword")).Value(requestPath))); |
|||
} |
|||
if (!correlationId.IsNullOrWhiteSpace()) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.Fields.CorrelationId.Suffix("keyword")).Value(correlationId))); |
|||
} |
|||
if (processId.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.Fields.ProcessId).Value(processId))); |
|||
} |
|||
if (threadId.HasValue) |
|||
{ |
|||
querys.Add((log) => log.Term((q) => q.Field(f => f.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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
namespace LINGYUN.Abp.Logging.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,38 @@ |
|||
namespace LINGYUN.Abp.Logging.Serilog.Elasticsearch |
|||
{ |
|||
public class SerilogField |
|||
{ |
|||
[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("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 LINGYUN.Abp.Logging.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,14 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" Version="4.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Logging |
|||
{ |
|||
public class AbpLoggingModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using LINGYUN.Abp.Logging; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging |
|||
{ |
|||
[Dependency(TryRegister = true)] |
|||
public class DefaultLoggingManager : ILoggingManager, ISingletonDependency |
|||
{ |
|||
public ILogger<DefaultLoggingManager> Logger { protected get; set; } |
|||
|
|||
public DefaultLoggingManager() |
|||
{ |
|||
Logger = NullLogger<DefaultLoggingManager>.Instance; |
|||
} |
|||
|
|||
public Task<LogInfo> GetAsync(string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogDebug("No logging manager is available!"); |
|||
LogInfo logInfo = null; |
|||
return Task.FromResult(logInfo); |
|||
} |
|||
|
|||
public Task<long> GetCountAsync(DateTime? startTime = null, DateTime? endTime = null, string context = null, string requestId = null, string requestPath = null, string correlationId = null, int? processId = null, int? threadId = null, bool? hasException = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogDebug("No logging manager is available!"); |
|||
return Task.FromResult(0L); |
|||
} |
|||
|
|||
public Task<List<LogInfo>> GetListAsync(string sorting = null, int maxResultCount = 50, int skipCount = 0, DateTime? startTime = null, DateTime? endTime = null, string context = null, string requestId = null, string requestPath = null, string correlationId = null, int? processId = null, int? threadId = null, bool? hasException = null, bool includeDetails = false, CancellationToken cancellationToken = default) |
|||
{ |
|||
Logger.LogDebug("No logging manager is available!"); |
|||
return Task.FromResult(new List<LogInfo>()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Logging |
|||
{ |
|||
public interface ILoggingManager |
|||
{ |
|||
Task<LogInfo> GetAsync( |
|||
string id, |
|||
CancellationToken cancellationToken = default(CancellationToken)); |
|||
|
|||
Task<long> GetCountAsync( |
|||
DateTime? startTime = null, |
|||
DateTime? endTime = 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(CancellationToken)); |
|||
|
|||
Task<List<LogInfo>> GetListAsync( |
|||
string sorting = null, |
|||
int maxResultCount = 50, |
|||
int skipCount = 0, |
|||
DateTime? startTime = null, |
|||
DateTime? endTime = 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(CancellationToken)); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Abp.Logging |
|||
{ |
|||
public class LogException |
|||
{ |
|||
public int Depth { get; set; } |
|||
public string Class { get; set; } |
|||
public string Message { get; set; } |
|||
public string Source { get; set; } |
|||
public string StackTrace { get; set; } |
|||
public int HResult { get; set; } |
|||
public string HelpURL { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
namespace LINGYUN.Abp.Logging |
|||
{ |
|||
public class LogField |
|||
{ |
|||
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,15 @@ |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Logging |
|||
{ |
|||
public class LogInfo |
|||
{ |
|||
public DateTime TimeStamp { get; set; } |
|||
public LogLevel Level { get; set; } |
|||
public string Message { get; set; } |
|||
public LogField Fields { get; set; } |
|||
public List<LogException> Exceptions { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<ShowAllFiles>false</ShowAllFiles> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> |
|||
<PackageReference Include="Moq.AutoMock" Version="3.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\auditing\LINGYUN.Abp.AuditLogging.Elasticsearch\LINGYUN.Abp.AuditLogging.Elasticsearch.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Moq.AutoMock; |
|||
using Nest; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
public abstract class AbpAuditLoggingElasticsearchTestBase : AbpTestsBase<AbpAuditLoggingElasticsearchTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using LINGYUN.Abp.Elasticsearch; |
|||
using LINGYUN.Abp.Tests; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpTestsBaseModule), |
|||
typeof(AbpAuditLoggingElasticsearchModule))] |
|||
public class AbpAuditLoggingElasticsearchTestModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configurationOptions = new AbpConfigurationBuilderOptions |
|||
{ |
|||
BasePath = @"D:\Projects\Development\Abp\AuditLogging\Elasticsearch", |
|||
EnvironmentName = "Development" |
|||
}; |
|||
|
|||
context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(configurationOptions)); |
|||
} |
|||
|
|||
public override void OnApplicationShutdown(ApplicationShutdownContext context) |
|||
{ |
|||
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpAuditLoggingElasticsearchOptions>>().Value; |
|||
var clientFactory = context.ServiceProvider.GetRequiredService<IElasticsearchClientFactory>(); |
|||
var client = clientFactory.Create(); |
|||
var indicesResponse = client.Cat.Indices(i => i.Index($"{options.IndexPrefix}-security-log")); |
|||
foreach (var index in indicesResponse.Records) |
|||
{ |
|||
client.Indices.Delete(index.Index); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
using Moq.AutoMock; |
|||
using Shouldly; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Auditing; |
|||
using Xunit; |
|||
|
|||
namespace LINGYUN.Abp.AuditLogging.Elasticsearch |
|||
{ |
|||
public class AuditLogManagerTests : AbpAuditLoggingElasticsearchTestBase |
|||
{ |
|||
private readonly IAuditLogManager _manager; |
|||
|
|||
public AuditLogManagerTests() |
|||
{ |
|||
_manager = GetRequiredService<IAuditLogManager>(); |
|||
} |
|||
|
|||
public async Task Save_Audit_Log_Should_Be_Find_By_Id() |
|||
{ |
|||
var mock = new AutoMocker(); |
|||
var auditLogInfo = mock.CreateInstance<AuditLogInfo>(); |
|||
|
|||
var id = await _manager.SaveAsync(auditLogInfo); |
|||
id.ShouldNotBeNullOrWhiteSpace(); |
|||
|
|||
var findId = Guid.Parse(id); |
|||
var auditLog = await _manager.GetAsync(findId); |
|||
|
|||
auditLog.ShouldNotBeNull(); |
|||
auditLog.Id.ShouldBe(findId); |
|||
|
|||
await _manager.DeleteAsync(findId); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Save_Audit_Log_Should_Get_List() |
|||
{ |
|||
//await MockcAsync(10);
|
|||
|
|||
// 异常应该只有3个
|
|||
(await _manager.GetCountAsync( |
|||
hasException: true)).ShouldBe(3); |
|||
|
|||
// 正常可以查询7个
|
|||
(await _manager.GetCountAsync( |
|||
hasException: false)).ShouldBe(7); |
|||
|
|||
// POST方法能查到5个
|
|||
(await _manager.GetCountAsync( |
|||
httpMethod: "POST")).ShouldBe(5); |
|||
|
|||
(await _manager.GetCountAsync( |
|||
startTime: DateTime.Now.AddDays(-1).AddHours(5))).ShouldBe(6); |
|||
|
|||
(await _manager.GetCountAsync( |
|||
endTime: DateTime.Now.AddDays(-1))).ShouldBe(4); |
|||
|
|||
(await _manager.GetCountAsync( |
|||
startTime: DateTime.Now.AddDays(-3).AddHours(1), |
|||
endTime: DateTime.Now)).ShouldBe(8); |
|||
|
|||
// 索引5只存在一个
|
|||
(await _manager.GetCountAsync( |
|||
userName: "_user_5", |
|||
clientId: "_client_5")).ShouldBe(1); |
|||
|
|||
var logs = await _manager.GetListAsync( |
|||
userName: "_user_5", |
|||
clientId: "_client_5"); |
|||
|
|||
logs.Count.ShouldBe(1); |
|||
logs[0].Url.ShouldBe("_url_5"); |
|||
logs[0].BrowserInfo.ShouldBe("_browser_5"); |
|||
logs[0].ApplicationName.ShouldBe("_app_5"); |
|||
} |
|||
|
|||
protected virtual async Task<List<string>> MockcAsync(int count) |
|||
{ |
|||
var mock = new AutoMocker(); |
|||
|
|||
var auditLogIds = new List<string>(); |
|||
|
|||
for (int i = 1; i <= count; i++) |
|||
{ |
|||
var auditLogInfo = mock.CreateInstance<AuditLogInfo>(); |
|||
auditLogInfo.ClientId = $"_client_{i}"; |
|||
auditLogInfo.Url = $"_url_{i}"; |
|||
auditLogInfo.UserName = $"_user_{i}"; |
|||
auditLogInfo.ApplicationName = $"_app_{i}"; |
|||
auditLogInfo.BrowserInfo = $"_browser_{i}"; |
|||
auditLogInfo.ExecutionTime = DateTime.Now; |
|||
|
|||
if (i % 3 == 0) |
|||
{ |
|||
auditLogInfo.Exceptions.Add(new Exception($"_exception_{i}")); |
|||
} |
|||
|
|||
if (i % 2 == 0) |
|||
{ |
|||
auditLogInfo.HttpMethod = "POST"; |
|||
} |
|||
|
|||
if (i % 4 == 0) |
|||
{ |
|||
auditLogInfo.ExecutionTime = DateTime.Now.AddDays(-3); |
|||
} |
|||
|
|||
if (i % 5 == 0) |
|||
{ |
|||
auditLogInfo.ExecutionTime = DateTime.Now.AddDays(-2); |
|||
} |
|||
|
|||
auditLogIds.Add(await _manager.SaveAsync(auditLogInfo)); |
|||
} |
|||
|
|||
return auditLogIds; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue