119 changed files with 5124 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,38 @@ |
|||||||
|
# LINGYUN.Abp.AuditLogging.Elasticsearch |
||||||
|
|
||||||
|
审计模块 Elasticsearch 实现 |
||||||
|
|
||||||
|
ElasticsearchAuditLogManager 实现了 IAuditLogManager, 审计日志由ES管理 |
||||||
|
ElasticsearchSecurityLogManager 实现了 ISecurityLogManager, 安全日志由ES管理 |
||||||
|
|
||||||
|
## 模块引用 |
||||||
|
|
||||||
|
|
||||||
|
```csharp |
||||||
|
[DependsOn(typeof(AbpAuditLoggingElasticsearchModule))] |
||||||
|
public class YouProjectModule : AbpModule |
||||||
|
{ |
||||||
|
// other |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 配置项 |
||||||
|
|
||||||
|
* AbpAuditLoggingElasticsearchOptions.IndexPrefix 索引前缀, 默认 auditlogging |
||||||
|
|
||||||
|
## 注意事项 |
||||||
|
|
||||||
|
与租户模块集成, 跨租户时将会切换索引 |
||||||
|
|
||||||
|
## appsettings.json |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"AuditLogging": { |
||||||
|
"Elasticsearch": { |
||||||
|
"IndexPrefix": "auditlogging" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
``` |
@ -0,0 +1,20 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\..\elasticsearch\Sanhe.Abp.Elasticsearch\Sanhe.Abp.Elasticsearch.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,22 @@ |
|||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Volo.Abp.Json; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpAuditLoggingModule), |
||||||
|
typeof(AbpElasticsearchModule), |
||||||
|
typeof(AbpJsonModule))] |
||||||
|
public class AbpAuditLoggingElasticsearchModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
var configuration = context.Services.GetConfiguration(); |
||||||
|
Configure<AbpAuditLoggingElasticsearchOptions>(configuration.GetSection("AuditLogging:Elasticsearch")); |
||||||
|
|
||||||
|
context.Services.AddHostedService<IndexInitializerService>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
using Nest; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public class AbpAuditLoggingElasticsearchOptions |
||||||
|
{ |
||||||
|
public const string DefaultIndexPrefix = "auditlogging"; |
||||||
|
public string IndexPrefix { get; set; } |
||||||
|
public IIndexSettings IndexSettings { get; set; } |
||||||
|
|
||||||
|
public AbpAuditLoggingElasticsearchOptions() |
||||||
|
{ |
||||||
|
IndexPrefix = DefaultIndexPrefix; |
||||||
|
IndexSettings = new IndexSettings(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.AspNetCore.ExceptionHandling; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.Data; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
using Volo.Abp.Http; |
||||||
|
using Volo.Abp.Json; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency |
||||||
|
{ |
||||||
|
protected IGuidGenerator GuidGenerator { get; } |
||||||
|
protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; } |
||||||
|
protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; } |
||||||
|
protected IJsonSerializer JsonSerializer { get; } |
||||||
|
|
||||||
|
public AuditLogInfoToAuditLogConverter( |
||||||
|
IGuidGenerator guidGenerator, |
||||||
|
IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions, |
||||||
|
IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, |
||||||
|
IJsonSerializer jsonSerializer) |
||||||
|
{ |
||||||
|
GuidGenerator = guidGenerator; |
||||||
|
ExceptionHandlingOptions = exceptionHandlingOptions.Value; |
||||||
|
ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter; |
||||||
|
JsonSerializer = jsonSerializer; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<AuditLog> ConvertAsync(AuditLogInfo auditLogInfo) |
||||||
|
{ |
||||||
|
var auditLogId = GuidGenerator.Create(); |
||||||
|
|
||||||
|
var extraProperties = new ExtraPropertyDictionary(); |
||||||
|
if (auditLogInfo.ExtraProperties != null) |
||||||
|
{ |
||||||
|
foreach (var pair in auditLogInfo.ExtraProperties) |
||||||
|
{ |
||||||
|
extraProperties.Add(pair.Key, pair.Value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var entityChanges = auditLogInfo |
||||||
|
.EntityChanges? |
||||||
|
.Select(entityChangeInfo => new EntityChange(GuidGenerator, auditLogId, entityChangeInfo, tenantId: auditLogInfo.TenantId)) |
||||||
|
.ToList() |
||||||
|
?? new List<EntityChange>(); |
||||||
|
|
||||||
|
var actions = auditLogInfo |
||||||
|
.Actions? |
||||||
|
.Select(auditLogActionInfo => new AuditLogAction(GuidGenerator.Create(), auditLogId, auditLogActionInfo, tenantId: auditLogInfo.TenantId)) |
||||||
|
.ToList() |
||||||
|
?? new List<AuditLogAction>(); |
||||||
|
|
||||||
|
var remoteServiceErrorInfos = auditLogInfo.Exceptions?.Select(exception => |
||||||
|
ExceptionToErrorInfoConverter.Convert(exception, options => |
||||||
|
{ |
||||||
|
options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients; |
||||||
|
options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients; |
||||||
|
})) ?? new List<RemoteServiceErrorInfo>(); |
||||||
|
|
||||||
|
var exceptions = remoteServiceErrorInfos.Any() |
||||||
|
? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true) |
||||||
|
: null; |
||||||
|
|
||||||
|
var comments = auditLogInfo |
||||||
|
.Comments? |
||||||
|
.JoinAsString(Environment.NewLine); |
||||||
|
|
||||||
|
var auditLog = new AuditLog( |
||||||
|
auditLogId, |
||||||
|
auditLogInfo.ApplicationName, |
||||||
|
auditLogInfo.TenantId, |
||||||
|
auditLogInfo.TenantName, |
||||||
|
auditLogInfo.UserId, |
||||||
|
auditLogInfo.UserName, |
||||||
|
auditLogInfo.ExecutionTime, |
||||||
|
auditLogInfo.ExecutionDuration, |
||||||
|
auditLogInfo.ClientIpAddress, |
||||||
|
auditLogInfo.ClientName, |
||||||
|
auditLogInfo.ClientId, |
||||||
|
auditLogInfo.CorrelationId, |
||||||
|
auditLogInfo.BrowserInfo, |
||||||
|
auditLogInfo.HttpMethod, |
||||||
|
auditLogInfo.Url, |
||||||
|
auditLogInfo.HttpStatusCode, |
||||||
|
auditLogInfo.ImpersonatorUserId, |
||||||
|
auditLogInfo.ImpersonatorTenantId, |
||||||
|
extraProperties, |
||||||
|
entityChanges, |
||||||
|
actions, |
||||||
|
exceptions, |
||||||
|
comments |
||||||
|
); |
||||||
|
|
||||||
|
return Task.FromResult(auditLog); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,362 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using Nest; |
||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Net; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class ElasticsearchAuditLogManager : IAuditLogManager, ITransientDependency |
||||||
|
{ |
||||||
|
private readonly AbpAuditingOptions _auditingOptions; |
||||||
|
private readonly AbpElasticsearchOptions _elasticsearchOptions; |
||||||
|
private readonly IIndexNameNormalizer _indexNameNormalizer; |
||||||
|
private readonly IElasticsearchClientFactory _clientFactory; |
||||||
|
private readonly IAuditLogInfoToAuditLogConverter _converter; |
||||||
|
|
||||||
|
public ILogger<ElasticsearchAuditLogManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public ElasticsearchAuditLogManager( |
||||||
|
IIndexNameNormalizer indexNameNormalizer, |
||||||
|
IOptions<AbpElasticsearchOptions> elasticsearchOptions, |
||||||
|
IElasticsearchClientFactory clientFactory, |
||||||
|
IOptions<AbpAuditingOptions> auditingOptions, |
||||||
|
IAuditLogInfoToAuditLogConverter converter) |
||||||
|
{ |
||||||
|
_converter = converter; |
||||||
|
_clientFactory = clientFactory; |
||||||
|
_auditingOptions = auditingOptions.Value; |
||||||
|
_elasticsearchOptions = elasticsearchOptions.Value; |
||||||
|
_indexNameNormalizer = indexNameNormalizer; |
||||||
|
|
||||||
|
Logger = NullLogger<ElasticsearchAuditLogManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public virtual async Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
httpMethod, |
||||||
|
url, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
applicationName, |
||||||
|
correlationId, |
||||||
|
clientId, |
||||||
|
clientIpAddress, |
||||||
|
maxExecutionDuration, |
||||||
|
minExecutionDuration, |
||||||
|
hasException, |
||||||
|
httpStatusCode); |
||||||
|
|
||||||
|
var response = await client.CountAsync<AuditLog>(dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => log.Bool(b => b.Must(querys.ToArray()))), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Count; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<List<AuditLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) |
||||||
|
? SortOrder.Ascending : SortOrder.Descending; |
||||||
|
sorting = !sorting.IsNullOrWhiteSpace() |
||||||
|
? sorting.Split()[0] |
||||||
|
: nameof(AuditLog.ExecutionTime); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
httpMethod, |
||||||
|
url, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
applicationName, |
||||||
|
correlationId, |
||||||
|
clientId, |
||||||
|
clientIpAddress, |
||||||
|
maxExecutionDuration, |
||||||
|
minExecutionDuration, |
||||||
|
hasException, |
||||||
|
httpStatusCode); |
||||||
|
|
||||||
|
SourceFilterDescriptor<AuditLog> SourceFilter(SourceFilterDescriptor<AuditLog> selector) |
||||||
|
{ |
||||||
|
selector.IncludeAll(); |
||||||
|
if (!includeDetails) |
||||||
|
{ |
||||||
|
selector.Excludes(field => |
||||||
|
field.Field(f => f.Actions) |
||||||
|
.Field(f => f.Comments) |
||||||
|
.Field(f => f.Exceptions) |
||||||
|
.Field(f => f.EntityChanges)); |
||||||
|
} |
||||||
|
|
||||||
|
return selector; |
||||||
|
} |
||||||
|
|
||||||
|
var response = await client.SearchAsync<AuditLog>(dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => log.Bool(b => b.Must(querys.ToArray()))) |
||||||
|
.Source(SourceFilter) |
||||||
|
.Sort(log => log.Field(GetField(sorting), sortOrder)) |
||||||
|
.From(skipCount) |
||||||
|
.Size(maxResultCount), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Documents.ToList(); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<AuditLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var response = await client.GetAsync<AuditLog>( |
||||||
|
id, |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Source; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
await client.DeleteAsync<AuditLog>( |
||||||
|
id, |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()), |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<string> SaveAsync( |
||||||
|
AuditLogInfo auditInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
if (!_auditingOptions.HideErrors) |
||||||
|
{ |
||||||
|
return await SaveLogAsync(auditInfo, cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
return await SaveLogAsync(auditInfo, cancellationToken); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Logger.LogWarning("Could not save the audit log object: " + Environment.NewLine + auditInfo.ToString()); |
||||||
|
Logger.LogException(ex, Microsoft.Extensions.Logging.LogLevel.Error); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual async Task<string> SaveLogAsync( |
||||||
|
AuditLogInfo auditLogInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var auditLog = await _converter.ConvertAsync(auditLogInfo); |
||||||
|
|
||||||
|
var response = await client.IndexAsync( |
||||||
|
auditLog, |
||||||
|
(x) => x.Index(CreateIndex()) |
||||||
|
.Id(auditLog.Id), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Id; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual List<Func<QueryContainerDescriptor<AuditLog>, QueryContainer>> BuildQueryDescriptor( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null) |
||||||
|
{ |
||||||
|
var querys = new List<Func<QueryContainerDescriptor<AuditLog>, QueryContainer>>(); |
||||||
|
|
||||||
|
if (startTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(AuditLog.ExecutionTime))).GreaterThanOrEquals(startTime))); |
||||||
|
} |
||||||
|
if (endTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(AuditLog.ExecutionTime))).LessThanOrEquals(endTime))); |
||||||
|
} |
||||||
|
if (!httpMethod.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.HttpMethod))).Value(httpMethod))); |
||||||
|
} |
||||||
|
if (!url.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Wildcard((q) => q.Field(GetField(nameof(AuditLog.Url))).Value($"*{url}*"))); |
||||||
|
} |
||||||
|
if (userId.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.UserId))).Value(userId))); |
||||||
|
} |
||||||
|
if (!userName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.UserName))).Value(userName))); |
||||||
|
} |
||||||
|
if (!applicationName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ApplicationName))).Value(applicationName))); |
||||||
|
} |
||||||
|
if (!correlationId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.CorrelationId))).Value(correlationId))); |
||||||
|
} |
||||||
|
if (!clientId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ClientId))).Value(clientId))); |
||||||
|
} |
||||||
|
if (!clientIpAddress.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.ClientIpAddress))).Value(clientIpAddress))); |
||||||
|
} |
||||||
|
if (maxExecutionDuration.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Range((q) => q.Field(GetField(nameof(AuditLog.ExecutionDuration))).LessThanOrEquals(maxExecutionDuration))); |
||||||
|
} |
||||||
|
if (minExecutionDuration.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Range((q) => q.Field(GetField(nameof(AuditLog.ExecutionDuration))).GreaterThanOrEquals(minExecutionDuration))); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (hasException.HasValue) |
||||||
|
{ |
||||||
|
if (hasException.Value) |
||||||
|
{ |
||||||
|
querys.Add( |
||||||
|
(q) => q.Bool( |
||||||
|
(b) => b.Must( |
||||||
|
(m) => m.Exists( |
||||||
|
(e) => e.Field((f) => f.Exceptions))) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
querys.Add( |
||||||
|
(q) => q.Bool( |
||||||
|
(b) => b.MustNot( |
||||||
|
(mn) => mn.Exists( |
||||||
|
(e) => e.Field( |
||||||
|
(f) => f.Exceptions))) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (httpStatusCode.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(AuditLog.HttpStatusCode))).Value(httpStatusCode))); |
||||||
|
} |
||||||
|
|
||||||
|
return querys; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual string CreateIndex() |
||||||
|
{ |
||||||
|
return _indexNameNormalizer.NormalizeIndex("audit-log"); |
||||||
|
} |
||||||
|
|
||||||
|
private readonly static IDictionary<string, string> _fieldMaps = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) |
||||||
|
{ |
||||||
|
{ "Id", "Id.keyword" }, |
||||||
|
{ "ApplicationName", "ApplicationName.keyword" }, |
||||||
|
{ "UserId", "UserId.keyword" }, |
||||||
|
{ "UserName", "UserName.keyword" }, |
||||||
|
{ "TenantId", "TenantId.keyword" }, |
||||||
|
{ "TenantName", "TenantName.keyword" }, |
||||||
|
{ "ImpersonatorUserId", "ImpersonatorUserId.keyword" }, |
||||||
|
{ "ImpersonatorTenantId", "ImpersonatorTenantId.keyword" }, |
||||||
|
{ "ClientName", "ClientName.keyword" }, |
||||||
|
{ "ClientIpAddress", "ClientIpAddress.keyword" }, |
||||||
|
{ "ClientId", "ClientId.keyword" }, |
||||||
|
{ "CorrelationId", "CorrelationId.keyword" }, |
||||||
|
{ "BrowserInfo", "BrowserInfo.keyword" }, |
||||||
|
{ "HttpMethod", "HttpMethod.keyword" }, |
||||||
|
{ "Url", "Url.keyword" }, |
||||||
|
{ "ExecutionDuration", "ExecutionDuration" }, |
||||||
|
{ "ExecutionTime", "ExecutionTime" }, |
||||||
|
{ "HttpStatusCode", "HttpStatusCode" }, |
||||||
|
}; |
||||||
|
protected virtual string GetField(string field) |
||||||
|
{ |
||||||
|
if (_fieldMaps.TryGetValue(field, out var mapField)) |
||||||
|
{ |
||||||
|
return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); |
||||||
|
} |
||||||
|
|
||||||
|
return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,371 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using Nest; |
||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch; |
||||||
|
|
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class ElasticsearchEntityChangeStore : IEntityChangeStore, ITransientDependency |
||||||
|
{ |
||||||
|
private readonly AbpElasticsearchOptions _elasticsearchOptions; |
||||||
|
private readonly IIndexNameNormalizer _indexNameNormalizer; |
||||||
|
private readonly IElasticsearchClientFactory _clientFactory; |
||||||
|
|
||||||
|
public ILogger<ElasticsearchEntityChangeStore> Logger { protected get; set; } |
||||||
|
|
||||||
|
public ElasticsearchEntityChangeStore( |
||||||
|
IIndexNameNormalizer indexNameNormalizer, |
||||||
|
IElasticsearchClientFactory clientFactory, |
||||||
|
IOptions<AbpElasticsearchOptions> elasticsearchOptions) |
||||||
|
{ |
||||||
|
_clientFactory = clientFactory; |
||||||
|
_indexNameNormalizer = indexNameNormalizer; |
||||||
|
_elasticsearchOptions = elasticsearchOptions.Value; |
||||||
|
|
||||||
|
Logger = NullLogger<ElasticsearchEntityChangeStore>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<EntityChange> GetAsync( |
||||||
|
Guid entityChangeId, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var resposne = await client.SearchAsync<EntityChange>( |
||||||
|
dsl => dsl.Index(CreateIndex()) |
||||||
|
.Query(query => |
||||||
|
query.Bool(bo => |
||||||
|
bo.Must(m => |
||||||
|
m.Nested(n => |
||||||
|
n.InnerHits() |
||||||
|
.Path("EntityChanges") |
||||||
|
.Query(nq => |
||||||
|
nq.Term(nqt => |
||||||
|
nqt.Field(GetField(nameof(EntityChange.Id))).Value(entityChangeId))))))) |
||||||
|
.Source(x => x.Excludes(f => f.Field("*"))) |
||||||
|
.Sort(entity => entity.Field("EntityChanges.ChangeTime", SortOrder.Descending)) |
||||||
|
.Size(1), |
||||||
|
ct: cancellationToken); |
||||||
|
|
||||||
|
if (resposne.Shards.Successful > 0) |
||||||
|
{ |
||||||
|
var hits = resposne.Hits.FirstOrDefault(); |
||||||
|
if (hits.InnerHits.Count > 0) |
||||||
|
{ |
||||||
|
return hits.InnerHits.First().Value.Documents<EntityChange>().FirstOrDefault(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<long> GetCountAsync( |
||||||
|
Guid? auditLogId = null, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
EntityChangeType? changeType = null, |
||||||
|
string entityId = null, |
||||||
|
string entityTypeFullName = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
await Task.CompletedTask; |
||||||
|
return 0; |
||||||
|
//var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
//var querys = BuildQueryDescriptor( |
||||||
|
// auditLogId, |
||||||
|
// startTime, |
||||||
|
// endTime, |
||||||
|
// changeType, |
||||||
|
// entityId, |
||||||
|
// entityTypeFullName); |
||||||
|
|
||||||
|
//Func<QueryContainerDescriptor<EntityChange>, QueryContainer> selector = q => q.MatchAll(); |
||||||
|
//if (querys.Count > 0) |
||||||
|
//{ |
||||||
|
// selector = q => q.Bool(b => b.Must(querys.ToArray())); |
||||||
|
//} |
||||||
|
|
||||||
|
//var response = await client.CountAsync<EntityChange>(dsl => |
||||||
|
// dsl.Index(CreateIndex()) |
||||||
|
// .Query(q => |
||||||
|
// q.Bool(b => |
||||||
|
// b.Must(m => |
||||||
|
// m.Nested(n => |
||||||
|
// n.InnerHits(hit => hit.Source(s => s.ExcludeAll())) |
||||||
|
// .Path("EntityChanges") |
||||||
|
// .Query(selector) |
||||||
|
// ) |
||||||
|
// ) |
||||||
|
// ) |
||||||
|
// ), |
||||||
|
// ct: cancellationToken); |
||||||
|
|
||||||
|
//return response.Count; |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<List<EntityChange>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
Guid? auditLogId = null, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
EntityChangeType? changeType = null, |
||||||
|
string entityId = null, |
||||||
|
string entityTypeFullName = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
// TODO: 需要解决Nested格式数据返回方式 |
||||||
|
|
||||||
|
//var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
//var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) |
||||||
|
// ? SortOrder.Ascending : SortOrder.Descending; |
||||||
|
//sorting = !sorting.IsNullOrWhiteSpace() |
||||||
|
// ? sorting.Split()[0] |
||||||
|
// : nameof(EntityChange.ChangeTime); |
||||||
|
|
||||||
|
//var querys = BuildQueryDescriptor( |
||||||
|
// auditLogId, |
||||||
|
// startTime, |
||||||
|
// endTime, |
||||||
|
// changeType, |
||||||
|
// entityId, |
||||||
|
// entityTypeFullName); |
||||||
|
|
||||||
|
//SourceFilterDescriptor<EntityChange> SourceFilter(SourceFilterDescriptor<EntityChange> selector) |
||||||
|
//{ |
||||||
|
// selector.Includes(GetEntityChangeSources()); |
||||||
|
// if (!includeDetails) |
||||||
|
// { |
||||||
|
// selector.Excludes(field => |
||||||
|
// field.Field("EntityChanges.PropertyChanges") |
||||||
|
// .Field("EntityChanges.ExtraProperties")); |
||||||
|
// } |
||||||
|
|
||||||
|
// return selector; |
||||||
|
//} |
||||||
|
|
||||||
|
//Func<QueryContainerDescriptor<EntityChange>, QueryContainer> selector = q => q.MatchAll(); |
||||||
|
//if (querys.Count > 0) |
||||||
|
//{ |
||||||
|
// selector = q => q.Bool(b => b.Must(querys.ToArray())); |
||||||
|
//} |
||||||
|
|
||||||
|
//var response = await client.SearchAsync<EntityChange>(dsl => |
||||||
|
// dsl.Index(CreateIndex()) |
||||||
|
// .Query(q => |
||||||
|
// q.Bool(b => |
||||||
|
// b.Must(m => |
||||||
|
// m.Nested(n => |
||||||
|
// n.InnerHits(hit => hit.Source(SourceFilter)) |
||||||
|
// .Path("EntityChanges") |
||||||
|
// .Query(selector) |
||||||
|
// ) |
||||||
|
// ) |
||||||
|
// ) |
||||||
|
// ) |
||||||
|
// .Source(x => x.Excludes(f => f.Field("*"))) |
||||||
|
// .Sort(entity => entity.Field(GetField(sorting), sortOrder)) |
||||||
|
// .From(skipCount) |
||||||
|
// .Size(maxResultCount), |
||||||
|
// cancellationToken); |
||||||
|
|
||||||
|
//if (response.Shards.Successful > 0) |
||||||
|
//{ |
||||||
|
// var hits = response.Hits.FirstOrDefault(); |
||||||
|
// if (hits.InnerHits.Count > 0) |
||||||
|
// { |
||||||
|
// return hits.InnerHits.First().Value.Documents<EntityChange>().ToList(); |
||||||
|
// } |
||||||
|
//} |
||||||
|
await Task.CompletedTask; |
||||||
|
return new List<EntityChange>(); |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<EntityChangeWithUsername> GetWithUsernameAsync( |
||||||
|
Guid entityChangeId, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var response = await client.SearchAsync<AuditLog>( |
||||||
|
dsl => dsl.Index(CreateIndex()) |
||||||
|
.Query(query => |
||||||
|
query.Bool(bo => |
||||||
|
bo.Must(m => |
||||||
|
m.Nested(n => |
||||||
|
n.InnerHits() |
||||||
|
.Path("EntityChanges") |
||||||
|
.Query(nq => |
||||||
|
nq.Bool(nb => |
||||||
|
nb.Must(nm => |
||||||
|
nm.Term(nt => |
||||||
|
nt.Field(GetField(nameof(EntityChange.Id))).Value(entityChangeId))))))))) |
||||||
|
.Source(selector => selector.Includes(field => |
||||||
|
field.Field(f => f.UserName))) |
||||||
|
.Size(1), |
||||||
|
ct: cancellationToken); |
||||||
|
|
||||||
|
var auditLog = response.Documents.FirstOrDefault(); |
||||||
|
EntityChange entityChange = null; |
||||||
|
|
||||||
|
if (response.Shards.Successful > 0) |
||||||
|
{ |
||||||
|
var hits = response.Hits.FirstOrDefault(); |
||||||
|
if (hits.InnerHits.Count > 0) |
||||||
|
{ |
||||||
|
entityChange = hits.InnerHits.First().Value.Documents<EntityChange>().FirstOrDefault(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return new EntityChangeWithUsername() |
||||||
|
{ |
||||||
|
EntityChange = entityChange, |
||||||
|
UserName = auditLog?.UserName |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<List<EntityChangeWithUsername>> GetWithUsernameAsync( |
||||||
|
string entityId, |
||||||
|
string entityTypeFullName, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var response = await client.SearchAsync<AuditLog>( |
||||||
|
dsl => dsl.Index(CreateIndex()) |
||||||
|
.Query(query => |
||||||
|
query.Bool(bo => |
||||||
|
bo.Must(m => |
||||||
|
m.Nested(n => |
||||||
|
n.InnerHits() |
||||||
|
.Path("EntityChanges") |
||||||
|
.Query(nq => |
||||||
|
nq.Bool(nb => |
||||||
|
nb.Must(nm => |
||||||
|
nm.Term(nt => |
||||||
|
nt.Field(GetField(nameof(EntityChange.EntityId))).Value(entityId)), |
||||||
|
nm => |
||||||
|
nm.Term(nt => |
||||||
|
nt.Field(GetField(nameof(EntityChange.EntityTypeFullName))).Value(entityTypeFullName)) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
.Source(selector => selector.Includes(field => |
||||||
|
field.Field(f => f.UserName))) |
||||||
|
.Sort(entity => entity.Field(f => f.ExecutionTime, SortOrder.Descending)), |
||||||
|
ct: cancellationToken); |
||||||
|
|
||||||
|
if (response.Hits.Count > 0) |
||||||
|
{ |
||||||
|
return response.Hits. |
||||||
|
Select(hit => new EntityChangeWithUsername |
||||||
|
{ |
||||||
|
UserName = hit.Source.UserName, |
||||||
|
EntityChange = hit.InnerHits.Any() ? |
||||||
|
hit.InnerHits.First().Value.Documents<EntityChange>().FirstOrDefault() |
||||||
|
: null |
||||||
|
}) |
||||||
|
.ToList(); |
||||||
|
} |
||||||
|
|
||||||
|
return new List<EntityChangeWithUsername>(); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual List<Func<QueryContainerDescriptor<EntityChange>, QueryContainer>> BuildQueryDescriptor( |
||||||
|
Guid? auditLogId = null, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
EntityChangeType? changeType = null, |
||||||
|
string entityId = null, |
||||||
|
string entityTypeFullName = null) |
||||||
|
{ |
||||||
|
var querys = new List<Func<QueryContainerDescriptor<EntityChange>, QueryContainer>>(); |
||||||
|
|
||||||
|
if (auditLogId.HasValue) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.AuditLogId))).Value(auditLogId))); |
||||||
|
} |
||||||
|
if (startTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.DateRange(q => q.Field(GetField(nameof(EntityChange.ChangeTime))).GreaterThanOrEquals(startTime))); |
||||||
|
} |
||||||
|
if (endTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.DateRange(q => q.Field(GetField(nameof(EntityChange.ChangeTime))).LessThanOrEquals(endTime))); |
||||||
|
} |
||||||
|
if (changeType.HasValue) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.ChangeType))).Value(changeType))); |
||||||
|
} |
||||||
|
if (!entityId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.Term(q => q.Field(GetField(nameof(EntityChange.EntityId))).Value(entityId))); |
||||||
|
} |
||||||
|
if (!entityTypeFullName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add(entity => entity.Wildcard(q => q.Field(GetField(nameof(EntityChange.EntityTypeFullName))).Value($"*{entityTypeFullName}*"))); |
||||||
|
} |
||||||
|
|
||||||
|
return querys; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual string CreateIndex() |
||||||
|
{ |
||||||
|
return _indexNameNormalizer.NormalizeIndex("audit-log"); |
||||||
|
} |
||||||
|
|
||||||
|
protected Func<FieldsDescriptor<EntityChange>, IPromise<Fields>> GetEntityChangeSources() |
||||||
|
{ |
||||||
|
return field => field |
||||||
|
.Field("EntityChanges.Id") |
||||||
|
.Field("EntityChanges.AuditLogId") |
||||||
|
.Field("EntityChanges.TenantId") |
||||||
|
.Field("EntityChanges.ChangeTime") |
||||||
|
.Field("EntityChanges.ChangeType") |
||||||
|
.Field("EntityChanges.EntityTenantId") |
||||||
|
.Field("EntityChanges.EntityId") |
||||||
|
.Field("EntityChanges.EntityTypeFullName") |
||||||
|
.Field("EntityChanges.PropertyChanges") |
||||||
|
.Field("EntityChanges.ExtraProperties"); |
||||||
|
} |
||||||
|
|
||||||
|
private readonly static IDictionary<string, string> _fieldMaps = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) |
||||||
|
{ |
||||||
|
{ "Id", "EntityChanges.Id.keyword" }, |
||||||
|
{ "AuditLogId", "EntityChanges.AuditLogId.keyword" }, |
||||||
|
{ "TenantId", "EntityChanges.TenantId.keyword" }, |
||||||
|
{ "EntityTenantId", "EntityChanges.EntityTenantId.keyword" }, |
||||||
|
{ "EntityId", "EntityChanges.EntityId.keyword" }, |
||||||
|
{ "EntityTypeFullName", "EntityChanges.EntityTypeFullName.keyword" }, |
||||||
|
{ "PropertyChanges", "EntityChanges.PropertyChanges" }, |
||||||
|
{ "ExtraProperties", "EntityChanges.ExtraProperties" }, |
||||||
|
{ "ChangeType", "EntityChanges.ChangeType" }, |
||||||
|
{ "ChangeTime", "EntityChanges.ChangeTime" }, |
||||||
|
}; |
||||||
|
protected virtual string GetField(string field) |
||||||
|
{ |
||||||
|
if (_fieldMaps.TryGetValue(field, out var mapField)) |
||||||
|
{ |
||||||
|
return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); |
||||||
|
} |
||||||
|
|
||||||
|
return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,268 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using Nest; |
||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class ElasticsearchSecurityLogManager : ISecurityLogManager, ITransientDependency |
||||||
|
{ |
||||||
|
private readonly AbpSecurityLogOptions _securityLogOptions; |
||||||
|
private readonly AbpElasticsearchOptions _elasticsearchOptions; |
||||||
|
private readonly IIndexNameNormalizer _indexNameNormalizer; |
||||||
|
private readonly IGuidGenerator _guidGenerator; |
||||||
|
private readonly IElasticsearchClientFactory _clientFactory; |
||||||
|
|
||||||
|
public ILogger<ElasticsearchSecurityLogManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public ElasticsearchSecurityLogManager( |
||||||
|
IGuidGenerator guidGenerator, |
||||||
|
IIndexNameNormalizer indexNameNormalizer, |
||||||
|
IOptions<AbpSecurityLogOptions> securityLogOptions, |
||||||
|
IOptions<AbpElasticsearchOptions> elasticsearchOptions, |
||||||
|
IElasticsearchClientFactory clientFactory) |
||||||
|
{ |
||||||
|
_guidGenerator = guidGenerator; |
||||||
|
_clientFactory = clientFactory; |
||||||
|
_indexNameNormalizer = indexNameNormalizer; |
||||||
|
_securityLogOptions = securityLogOptions.Value; |
||||||
|
_elasticsearchOptions = elasticsearchOptions.Value; |
||||||
|
|
||||||
|
Logger = NullLogger<ElasticsearchSecurityLogManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task SaveAsync( |
||||||
|
SecurityLogInfo securityLogInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
// TODO: 框架不把这玩意儿放在 ISecurityLogManager? |
||||||
|
if (!_securityLogOptions.IsEnabled) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var securityLog = new SecurityLog( |
||||||
|
_guidGenerator.Create(), |
||||||
|
securityLogInfo); |
||||||
|
|
||||||
|
await client.IndexAsync( |
||||||
|
securityLog, |
||||||
|
(x) => x.Index(CreateIndex()) |
||||||
|
.Id(securityLog.Id), |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<SecurityLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var response = await client.GetAsync<SecurityLog>( |
||||||
|
id, |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Source; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
await client.DeleteAsync<SecurityLog>( |
||||||
|
id, |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()), |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<List<SecurityLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) |
||||||
|
? SortOrder.Ascending : SortOrder.Descending; |
||||||
|
sorting = !sorting.IsNullOrWhiteSpace() |
||||||
|
? sorting.Split()[0] |
||||||
|
: nameof(SecurityLog.CreationTime); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
applicationName, |
||||||
|
identity, |
||||||
|
action, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
clientId, |
||||||
|
clientIpAddress, |
||||||
|
correlationId); |
||||||
|
|
||||||
|
var response = await client.SearchAsync<SecurityLog>(dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => log.Bool(b => b.Must(querys.ToArray()))) |
||||||
|
.Source(log => log.IncludeAll()) |
||||||
|
.Sort(log => log.Field(GetField(sorting), sortOrder)) |
||||||
|
.From(skipCount) |
||||||
|
.Size(maxResultCount), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Documents.ToList(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public virtual async Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
applicationName, |
||||||
|
identity, |
||||||
|
action, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
clientId, |
||||||
|
clientIpAddress, |
||||||
|
correlationId); |
||||||
|
|
||||||
|
var response = await client.CountAsync<SecurityLog>(dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => log.Bool(b => b.Must(querys.ToArray()))), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Count; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual List<Func<QueryContainerDescriptor<SecurityLog>, QueryContainer>> BuildQueryDescriptor( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null) |
||||||
|
{ |
||||||
|
var querys = new List<Func<QueryContainerDescriptor<SecurityLog>, QueryContainer>>(); |
||||||
|
|
||||||
|
if (startTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SecurityLog.CreationTime))).GreaterThanOrEquals(startTime))); |
||||||
|
} |
||||||
|
if (endTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SecurityLog.CreationTime))).LessThanOrEquals(endTime))); |
||||||
|
} |
||||||
|
if (!applicationName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ApplicationName))).Value(applicationName))); |
||||||
|
} |
||||||
|
if (!identity.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.Identity))).Value(identity))); |
||||||
|
} |
||||||
|
if (!action.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.Action))).Value(action))); |
||||||
|
} |
||||||
|
if (userId.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.UserId))).Value(userId))); |
||||||
|
} |
||||||
|
if (!userName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.UserName))).Value(userName))); |
||||||
|
} |
||||||
|
if (!clientId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ClientId))).Value(clientId))); |
||||||
|
} |
||||||
|
if (!clientIpAddress.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.ClientIpAddress))).Value(clientIpAddress))); |
||||||
|
} |
||||||
|
if (!correlationId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SecurityLog.CorrelationId))).Value(correlationId))); |
||||||
|
} |
||||||
|
|
||||||
|
return querys; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual string CreateIndex() |
||||||
|
{ |
||||||
|
return _indexNameNormalizer.NormalizeIndex("security-log"); |
||||||
|
} |
||||||
|
|
||||||
|
private readonly static IDictionary<string, string> _fieldMaps = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) |
||||||
|
{ |
||||||
|
{ "Id", "Id.keyword" }, |
||||||
|
{ "ApplicationName", "ApplicationName.keyword" }, |
||||||
|
{ "UserId", "UserId.keyword" }, |
||||||
|
{ "UserName", "UserName.keyword" }, |
||||||
|
{ "TenantId", "TenantId.keyword" }, |
||||||
|
{ "TenantName", "TenantName.keyword" }, |
||||||
|
{ "Identity", "Identity.keyword" }, |
||||||
|
{ "Action", "Action.keyword" }, |
||||||
|
{ "BrowserInfo", "BrowserInfo.keyword" }, |
||||||
|
{ "ClientIpAddress", "ClientIpAddress.keyword" }, |
||||||
|
{ "ClientId", "ClientId.keyword" }, |
||||||
|
{ "CorrelationId", "CorrelationId.keyword" }, |
||||||
|
{ "CreationTime", "CreationTime" }, |
||||||
|
}; |
||||||
|
protected virtual string GetField(string field) |
||||||
|
{ |
||||||
|
if (_fieldMaps.TryGetValue(field, out var mapField)) |
||||||
|
{ |
||||||
|
return _elasticsearchOptions.FieldCamelCase ? mapField.ToCamelCase() : mapField.ToPascalCase(); |
||||||
|
} |
||||||
|
|
||||||
|
return _elasticsearchOptions.FieldCamelCase ? field.ToCamelCase() : field.ToPascalCase(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public interface IAuditLogInfoToAuditLogConverter |
||||||
|
{ |
||||||
|
Task<AuditLog> ConvertAsync(AuditLogInfo auditLogInfo); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public interface IIndexInitializer |
||||||
|
{ |
||||||
|
Task InitializeAsync(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public interface IIndexNameNormalizer |
||||||
|
{ |
||||||
|
string NormalizeIndex(string index); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using Nest; |
||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Data; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.Json; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public class IndexInitializer : IIndexInitializer, ISingletonDependency |
||||||
|
{ |
||||||
|
private readonly AbpJsonOptions _jsonOptions; |
||||||
|
private readonly AbpAuditLoggingElasticsearchOptions _elasticsearchOptions; |
||||||
|
private readonly IIndexNameNormalizer _nameNormalizer; |
||||||
|
private readonly IElasticsearchClientFactory _clientFactory; |
||||||
|
|
||||||
|
public ILogger<IndexInitializer> Logger { protected get; set; } |
||||||
|
|
||||||
|
public IndexInitializer( |
||||||
|
IOptions<AbpJsonOptions> jsonOptions, |
||||||
|
IOptions<AbpAuditLoggingElasticsearchOptions> elasticsearchOptions, |
||||||
|
IIndexNameNormalizer nameNormalizer, |
||||||
|
IElasticsearchClientFactory clientFactory) |
||||||
|
{ |
||||||
|
_jsonOptions = jsonOptions.Value; |
||||||
|
_elasticsearchOptions = elasticsearchOptions.Value; |
||||||
|
_nameNormalizer = nameNormalizer; |
||||||
|
_clientFactory = clientFactory; |
||||||
|
|
||||||
|
Logger = NullLogger<IndexInitializer>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task InitializeAsync() |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
var dateTimeFormat = !_jsonOptions.DefaultDateTimeFormat.IsNullOrWhiteSpace() |
||||||
|
? $"{_jsonOptions.DefaultDateTimeFormat}||strict_date_optional_time||epoch_millis" |
||||||
|
: "strict_date_optional_time||epoch_millis"; |
||||||
|
var indexState = new IndexState |
||||||
|
{ |
||||||
|
Settings = _elasticsearchOptions.IndexSettings, |
||||||
|
}; |
||||||
|
await InitlizeAuditLogIndex(client, indexState, dateTimeFormat); |
||||||
|
await InitlizeSecurityLogIndex(client, indexState, dateTimeFormat); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual async Task InitlizeAuditLogIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat) |
||||||
|
{ |
||||||
|
var indexName = _nameNormalizer.NormalizeIndex("audit-log"); |
||||||
|
var indexExists = await client.Indices.ExistsAsync(indexName); |
||||||
|
if (!indexExists.Exists) |
||||||
|
{ |
||||||
|
var indexCreateResponse = await client.Indices.CreateAsync( |
||||||
|
indexName, |
||||||
|
dsl => dsl.InitializeUsing(indexState) |
||||||
|
.Map<AuditLog>(map => |
||||||
|
map.AutoMap() |
||||||
|
.Properties(mp => |
||||||
|
mp.Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat)) |
||||||
|
.Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) |
||||||
|
.Nested<EntityChange>(n => |
||||||
|
n.AutoMap() |
||||||
|
.Name(nameof(AuditLog.EntityChanges)) |
||||||
|
.Properties(np => |
||||||
|
np.Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) |
||||||
|
.Date(p => p.Name(n => n.ChangeTime).Format(dateTimeFormat)) |
||||||
|
.Nested<EntityPropertyChange>(npn => npn.Name(nameof(EntityChange.PropertyChanges))))) |
||||||
|
.Nested<AuditLogAction>(n => n.Name(nameof(AuditLog.Actions)) |
||||||
|
.AutoMap() |
||||||
|
.Properties(np => |
||||||
|
np.Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) |
||||||
|
.Date(p => p.Name(n => n.ExecutionTime).Format(dateTimeFormat))))))); |
||||||
|
if (!indexCreateResponse.IsValid) |
||||||
|
{ |
||||||
|
Logger.LogWarning("Failed to initialize index and audit log may not be retrieved."); |
||||||
|
Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual async Task InitlizeSecurityLogIndex(IElasticClient client, IIndexState indexState, string dateTimeFormat) |
||||||
|
{ |
||||||
|
var indexName = _nameNormalizer.NormalizeIndex("security-log"); |
||||||
|
var indexExists = await client.Indices.ExistsAsync(indexName); |
||||||
|
if (!indexExists.Exists) |
||||||
|
{ |
||||||
|
var indexCreateResponse = await client.Indices.CreateAsync( |
||||||
|
indexName, |
||||||
|
dsl => dsl.InitializeUsing(indexState) |
||||||
|
.Map<SecurityLog>(map => |
||||||
|
map.AutoMap() |
||||||
|
.Properties(mp => |
||||||
|
mp.Object<ExtraPropertyDictionary>(p => p.Name(n => n.ExtraProperties)) |
||||||
|
.Date(p => p.Name(n => n.CreationTime).Format(dateTimeFormat))))); |
||||||
|
if (!indexCreateResponse.IsValid) |
||||||
|
{ |
||||||
|
Logger.LogWarning("Failed to initialize index and security log may not be retrieved."); |
||||||
|
Logger.LogWarning(indexCreateResponse.OriginalException.ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
using Microsoft.Extensions.Hosting; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public class IndexInitializerService : BackgroundService |
||||||
|
{ |
||||||
|
private readonly IIndexInitializer _indexInitializer; |
||||||
|
|
||||||
|
public IndexInitializerService(IIndexInitializer indexInitializer) |
||||||
|
{ |
||||||
|
_indexInitializer = indexInitializer; |
||||||
|
} |
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken) |
||||||
|
{ |
||||||
|
await _indexInitializer.InitializeAsync(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using System; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.MultiTenancy; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Elasticsearch |
||||||
|
{ |
||||||
|
public class IndexNameNormalizer : IIndexNameNormalizer, ISingletonDependency |
||||||
|
{ |
||||||
|
private readonly ICurrentTenant _currentTenant; |
||||||
|
private readonly AbpAuditLoggingElasticsearchOptions _options; |
||||||
|
|
||||||
|
public IndexNameNormalizer(ICurrentTenant currentTenant, IOptions<AbpAuditLoggingElasticsearchOptions> options) |
||||||
|
{ |
||||||
|
_currentTenant = currentTenant; |
||||||
|
_options = options.Value; |
||||||
|
} |
||||||
|
|
||||||
|
public string NormalizeIndex(string index) |
||||||
|
{ |
||||||
|
if (_currentTenant.IsAvailable) |
||||||
|
{ |
||||||
|
return $"{_options.IndexPrefix}-{index}-{_currentTenant.Id:N}"; |
||||||
|
} |
||||||
|
return _options.IndexPrefix.IsNullOrWhiteSpace() |
||||||
|
? index |
||||||
|
: $"{_options.IndexPrefix}-{index}"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,32 @@ |
|||||||
|
# LINGYUN.Abp.AuditLogging.EntityFrameworkCore |
||||||
|
|
||||||
|
审计模块 EntityFrameworkCore 实现, 此模块仅作为桥梁, 具体实现交给abp官方模块 |
||||||
|
|
||||||
|
AuditLogManager 实现了 IAuditLogManager, 审计日志由Volo.Abp.AuditLogging模块管理 |
||||||
|
SecurityLogManager 实现了 ISecurityLogManager, 安全日志由Volo.Abp.Identity模块管理 |
||||||
|
|
||||||
|
## 模块引用 |
||||||
|
|
||||||
|
```csharp |
||||||
|
[DependsOn(typeof(AbpAuditLoggingEntityFrameworkCoreModule))] |
||||||
|
public class YouProjectModule : AbpModule |
||||||
|
{ |
||||||
|
// other |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 配置项 |
||||||
|
|
||||||
|
请遵循 Volo.Abp.AuditLogging、Volo.Abp.Identity模块中的配置 |
||||||
|
|
||||||
|
## appsettings.json |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"ConnectionStrings": { |
||||||
|
"AbpIdentity": "Server=127.0.0.1;Database=Identity;User Id=root;Password=*", |
||||||
|
"AbpAuditLogging": "Server=127.0.0.1;Database=AuditLogging;User Id=root;Password=*", |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
``` |
@ -0,0 +1,21 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>net6.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,25 @@ |
|||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Volo.Abp.AutoMapper; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(Volo.Abp.Identity.EntityFrameworkCore.AbpIdentityEntityFrameworkCoreModule), |
||||||
|
typeof(Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingEntityFrameworkCoreModule))] |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpAuditLoggingModule), |
||||||
|
typeof(AbpAutoMapperModule))] |
||||||
|
public class AbpAuditLoggingEntityFrameworkCoreModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
context.Services.AddAutoMapperObjectMapper<AbpAuditLoggingEntityFrameworkCoreModule>(); |
||||||
|
|
||||||
|
Configure<AbpAutoMapperOptions>(options => |
||||||
|
{ |
||||||
|
options.AddProfile<AbpAuditingMapperProfile>(validate: true); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
using AutoMapper; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore |
||||||
|
{ |
||||||
|
public class AbpAuditingMapperProfile : Profile |
||||||
|
{ |
||||||
|
public AbpAuditingMapperProfile() |
||||||
|
{ |
||||||
|
CreateMap<Volo.Abp.AuditLogging.AuditLogAction, AuditLogAction>() |
||||||
|
.MapExtraProperties(); |
||||||
|
CreateMap<Volo.Abp.AuditLogging.EntityPropertyChange, EntityPropertyChange>(); |
||||||
|
CreateMap<Volo.Abp.AuditLogging.EntityChange, EntityChange>() |
||||||
|
.MapExtraProperties(); |
||||||
|
CreateMap<Volo.Abp.AuditLogging.AuditLog, AuditLog>() |
||||||
|
.MapExtraProperties(); |
||||||
|
|
||||||
|
CreateMap<Volo.Abp.Identity.IdentitySecurityLog, SecurityLog>() |
||||||
|
.MapExtraProperties(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Net; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.AuditLogging; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.ObjectMapping; |
||||||
|
using Volo.Abp.Uow; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class AuditLogManager : IAuditLogManager, ISingletonDependency |
||||||
|
{ |
||||||
|
protected IObjectMapper ObjectMapper { get; } |
||||||
|
protected IAuditLogRepository AuditLogRepository { get; } |
||||||
|
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
||||||
|
protected AbpAuditingOptions Options { get; } |
||||||
|
protected IAuditLogInfoToAuditLogConverter Converter { get; } |
||||||
|
|
||||||
|
public ILogger<AuditLogManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public AuditLogManager( |
||||||
|
IObjectMapper objectMapper, |
||||||
|
IAuditLogRepository auditLogRepository, |
||||||
|
IUnitOfWorkManager unitOfWorkManager, |
||||||
|
IOptions<AbpAuditingOptions> options, |
||||||
|
IAuditLogInfoToAuditLogConverter converter) |
||||||
|
{ |
||||||
|
ObjectMapper = objectMapper; |
||||||
|
AuditLogRepository = auditLogRepository; |
||||||
|
UnitOfWorkManager = unitOfWorkManager; |
||||||
|
Converter = converter; |
||||||
|
Options = options.Value; |
||||||
|
|
||||||
|
Logger = NullLogger<AuditLogManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public virtual async Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
return await AuditLogRepository.GetCountAsync( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
httpMethod, |
||||||
|
url, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
applicationName, |
||||||
|
clientIpAddress, |
||||||
|
correlationId, |
||||||
|
maxExecutionDuration, |
||||||
|
minExecutionDuration, |
||||||
|
hasException, |
||||||
|
httpStatusCode, |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<List<AuditLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var auditLogs = await AuditLogRepository.GetListAsync( |
||||||
|
sorting, |
||||||
|
maxResultCount, |
||||||
|
skipCount, |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
httpMethod, |
||||||
|
url, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
applicationName, |
||||||
|
clientIpAddress, |
||||||
|
correlationId, |
||||||
|
maxExecutionDuration, |
||||||
|
minExecutionDuration, |
||||||
|
hasException, |
||||||
|
httpStatusCode, |
||||||
|
includeDetails, |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return ObjectMapper.Map<List<Volo.Abp.AuditLogging.AuditLog>, List<AuditLog>>(auditLogs); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<AuditLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var auditLog = await AuditLogRepository.GetAsync(id, includeDetails, cancellationToken); |
||||||
|
|
||||||
|
return ObjectMapper.Map<Volo.Abp.AuditLogging.AuditLog, AuditLog>(auditLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
using (var uow = UnitOfWorkManager.Begin(true)) |
||||||
|
{ |
||||||
|
await AuditLogRepository.DeleteAsync(id); |
||||||
|
await uow.CompleteAsync(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<string> SaveAsync( |
||||||
|
AuditLogInfo auditInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
if (!Options.HideErrors) |
||||||
|
{ |
||||||
|
return await SaveLogAsync(auditInfo, cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
return await SaveLogAsync(auditInfo, cancellationToken); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
Logger.LogWarning("Could not save the audit log object: " + Environment.NewLine + auditInfo.ToString()); |
||||||
|
Logger.LogException(ex, LogLevel.Error); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual async Task<string> SaveLogAsync( |
||||||
|
AuditLogInfo auditInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
using (var uow = UnitOfWorkManager.Begin(true)) |
||||||
|
{ |
||||||
|
var auditLog = await AuditLogRepository.InsertAsync( |
||||||
|
await Converter.ConvertAsync(auditInfo), |
||||||
|
false, |
||||||
|
cancellationToken); |
||||||
|
await uow.CompleteAsync(); |
||||||
|
|
||||||
|
return auditLog.Id.ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
using Volo.Abp.Identity; |
||||||
|
using Volo.Abp.ObjectMapping; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
using Volo.Abp.Uow; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.EntityFrameworkCore |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class SecurityLogManager : ISecurityLogManager, ISingletonDependency |
||||||
|
{ |
||||||
|
public ILogger<SecurityLogManager> Logger { get; set; } |
||||||
|
|
||||||
|
protected IObjectMapper ObjectMapper { get; } |
||||||
|
protected AbpSecurityLogOptions SecurityLogOptions { get; } |
||||||
|
protected IIdentitySecurityLogRepository IdentitySecurityLogRepository { get; } |
||||||
|
protected IGuidGenerator GuidGenerator { get; } |
||||||
|
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
||||||
|
|
||||||
|
public SecurityLogManager( |
||||||
|
IObjectMapper objectMapper, |
||||||
|
ILogger<SecurityLogManager> logger, |
||||||
|
IOptions<AbpSecurityLogOptions> securityLogOptions, |
||||||
|
IIdentitySecurityLogRepository identitySecurityLogRepository, |
||||||
|
IGuidGenerator guidGenerator, |
||||||
|
IUnitOfWorkManager unitOfWorkManager) |
||||||
|
{ |
||||||
|
Logger = logger; |
||||||
|
ObjectMapper = objectMapper; |
||||||
|
SecurityLogOptions = securityLogOptions.Value; |
||||||
|
IdentitySecurityLogRepository = identitySecurityLogRepository; |
||||||
|
GuidGenerator = guidGenerator; |
||||||
|
UnitOfWorkManager = unitOfWorkManager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task SaveAsync( |
||||||
|
SecurityLogInfo securityLogInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
if (!SecurityLogOptions.IsEnabled) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) |
||||||
|
{ |
||||||
|
await IdentitySecurityLogRepository.InsertAsync( |
||||||
|
new IdentitySecurityLog(GuidGenerator, securityLogInfo), |
||||||
|
false, |
||||||
|
cancellationToken); |
||||||
|
await uow.CompleteAsync(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<SecurityLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var securityLog = await IdentitySecurityLogRepository.GetAsync(id, includeDetails, cancellationToken); |
||||||
|
|
||||||
|
return ObjectMapper.Map<IdentitySecurityLog, SecurityLog>(securityLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
using (var uow = UnitOfWorkManager.Begin(true)) |
||||||
|
{ |
||||||
|
await IdentitySecurityLogRepository.DeleteAsync(id); |
||||||
|
await uow.CompleteAsync(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<List<SecurityLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var securityLogs = await IdentitySecurityLogRepository.GetListAsync( |
||||||
|
sorting, |
||||||
|
maxResultCount, |
||||||
|
skipCount, |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
applicationName, |
||||||
|
identity, |
||||||
|
action, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
clientId, |
||||||
|
correlationId, |
||||||
|
includeDetails, |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return ObjectMapper.Map<List<IdentitySecurityLog>, List<SecurityLog>>(securityLogs); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public virtual async Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
return await IdentitySecurityLogRepository.GetCountAsync( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
applicationName, |
||||||
|
identity, |
||||||
|
action, |
||||||
|
userId, |
||||||
|
userName, |
||||||
|
clientId, |
||||||
|
correlationId, |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,17 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.Auditing" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Guids" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.ExceptionHandling" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,15 @@ |
|||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.ExceptionHandling; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpAuditingModule), |
||||||
|
typeof(AbpGuidsModule), |
||||||
|
typeof(AbpExceptionHandlingModule))] |
||||||
|
public class AbpAuditLoggingModule : AbpModule |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.Data; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[DisableAuditing] |
||||||
|
public class AuditLog : IHasExtraProperties |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
|
||||||
|
public string ApplicationName { get; set; } |
||||||
|
|
||||||
|
public Guid? UserId { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public string TenantName { get; set; } |
||||||
|
|
||||||
|
public Guid? ImpersonatorUserId { get; set; } |
||||||
|
|
||||||
|
public Guid? ImpersonatorTenantId { get; set; } |
||||||
|
|
||||||
|
public DateTime ExecutionTime { get; set; } |
||||||
|
|
||||||
|
public int ExecutionDuration { get; set; } |
||||||
|
|
||||||
|
public string ClientIpAddress { get; set; } |
||||||
|
|
||||||
|
public string ClientName { get; set; } |
||||||
|
|
||||||
|
public string ClientId { get; set; } |
||||||
|
|
||||||
|
public string CorrelationId { get; set; } |
||||||
|
|
||||||
|
public string BrowserInfo { get; set; } |
||||||
|
|
||||||
|
public string HttpMethod { get; set; } |
||||||
|
|
||||||
|
public string Url { get; set; } |
||||||
|
|
||||||
|
public string Exceptions { get; set; } |
||||||
|
|
||||||
|
public string Comments { get; set; } |
||||||
|
|
||||||
|
public int? HttpStatusCode { get; set; } |
||||||
|
|
||||||
|
public List<EntityChange> EntityChanges { get; set; } |
||||||
|
|
||||||
|
public List<AuditLogAction> Actions { get; set; } |
||||||
|
|
||||||
|
public ExtraPropertyDictionary ExtraProperties { get; set; } |
||||||
|
|
||||||
|
public AuditLog() |
||||||
|
{ |
||||||
|
Actions = new List<AuditLogAction>(); |
||||||
|
EntityChanges = new List<EntityChange>(); |
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
} |
||||||
|
|
||||||
|
public AuditLog( |
||||||
|
Guid id, |
||||||
|
string applicationName, |
||||||
|
Guid? tenantId, |
||||||
|
string tenantName, |
||||||
|
Guid? userId, |
||||||
|
string userName, |
||||||
|
DateTime executionTime, |
||||||
|
int executionDuration, |
||||||
|
string clientIpAddress, |
||||||
|
string clientName, |
||||||
|
string clientId, |
||||||
|
string correlationId, |
||||||
|
string browserInfo, |
||||||
|
string httpMethod, |
||||||
|
string url, |
||||||
|
int? httpStatusCode, |
||||||
|
Guid? impersonatorUserId, |
||||||
|
Guid? impersonatorTenantId, |
||||||
|
ExtraPropertyDictionary extraPropertyDictionary, |
||||||
|
List<EntityChange> entityChanges, |
||||||
|
List<AuditLogAction> actions, |
||||||
|
string exceptions, |
||||||
|
string comments) |
||||||
|
{ |
||||||
|
Id = id; |
||||||
|
ApplicationName = applicationName; |
||||||
|
TenantId = tenantId; |
||||||
|
TenantName = tenantName; |
||||||
|
UserId = userId; |
||||||
|
UserName = userName; |
||||||
|
ExecutionTime = executionTime; |
||||||
|
ExecutionDuration = executionDuration; |
||||||
|
ClientIpAddress = clientIpAddress; |
||||||
|
ClientName = clientName; |
||||||
|
ClientId = clientId; |
||||||
|
CorrelationId = correlationId; |
||||||
|
BrowserInfo = browserInfo; |
||||||
|
HttpMethod = httpMethod; |
||||||
|
Url = url; |
||||||
|
HttpStatusCode = httpStatusCode; |
||||||
|
ImpersonatorUserId = impersonatorUserId; |
||||||
|
ImpersonatorTenantId = impersonatorTenantId; |
||||||
|
|
||||||
|
ExtraProperties = extraPropertyDictionary; |
||||||
|
EntityChanges = entityChanges; |
||||||
|
Actions = actions; |
||||||
|
Exceptions = exceptions; |
||||||
|
Comments = comments; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.Data; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[DisableAuditing] |
||||||
|
public class AuditLogAction : IHasExtraProperties |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public Guid AuditLogId { get; set; } |
||||||
|
|
||||||
|
public string ServiceName { get; set; } |
||||||
|
|
||||||
|
public string MethodName { get; set; } |
||||||
|
|
||||||
|
public string Parameters { get; set; } |
||||||
|
|
||||||
|
public DateTime ExecutionTime { get; set; } |
||||||
|
|
||||||
|
public int ExecutionDuration { get; set; } |
||||||
|
|
||||||
|
public ExtraPropertyDictionary ExtraProperties { get; set; } |
||||||
|
|
||||||
|
public AuditLogAction() |
||||||
|
{ |
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
} |
||||||
|
|
||||||
|
public AuditLogAction(Guid id, Guid auditLogId, AuditLogActionInfo actionInfo, Guid? tenantId = null) |
||||||
|
{ |
||||||
|
|
||||||
|
Id = id; |
||||||
|
TenantId = tenantId; |
||||||
|
AuditLogId = auditLogId; |
||||||
|
ExecutionTime = actionInfo.ExecutionTime; |
||||||
|
ExecutionDuration = actionInfo.ExecutionDuration; |
||||||
|
ExtraProperties = new ExtraPropertyDictionary(actionInfo.ExtraProperties); |
||||||
|
ServiceName = actionInfo.ServiceName; |
||||||
|
MethodName = actionInfo.MethodName; |
||||||
|
Parameters = actionInfo.Parameters.Length > 2000 ? "" : actionInfo.Parameters; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class AuditingStore : IAuditingStore, ITransientDependency |
||||||
|
{ |
||||||
|
private readonly IAuditLogManager _manager; |
||||||
|
|
||||||
|
public AuditingStore( |
||||||
|
IAuditLogManager manager) |
||||||
|
{ |
||||||
|
_manager = manager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task SaveAsync(AuditLogInfo auditInfo) |
||||||
|
{ |
||||||
|
await _manager.SaveAsync(auditInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Net; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[Dependency(TryRegister = true)] |
||||||
|
public class DefaultAuditLogManager : IAuditLogManager, ISingletonDependency |
||||||
|
{ |
||||||
|
public ILogger<DefaultAuditLogManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public DefaultAuditLogManager() |
||||||
|
{ |
||||||
|
Logger = NullLogger<DefaultAuditLogManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No audit log manager is available!"); |
||||||
|
return Task.FromResult(0L); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<List<AuditLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No audit log manager is available!"); |
||||||
|
return Task.FromResult(new List<AuditLog>()); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<string> SaveAsync( |
||||||
|
AuditLogInfo auditInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No audit log manager is available and is written to the local log by default"); |
||||||
|
Logger.LogInformation(auditInfo.ToString()); |
||||||
|
|
||||||
|
return Task.FromResult(""); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<AuditLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No audit log manager is available!"); |
||||||
|
|
||||||
|
AuditLog auditLog = null; |
||||||
|
return Task.FromResult(auditLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No audit log manager is available!"); |
||||||
|
return Task.CompletedTask; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[Dependency(TryRegister = true)] |
||||||
|
public class DefaultEntityChangeStore : IEntityChangeStore, ISingletonDependency |
||||||
|
{ |
||||||
|
public Task<EntityChange> GetAsync(Guid entityChangeId, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
EntityChange entityChange = null; |
||||||
|
return Task.FromResult(entityChange); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<long> GetCountAsync(Guid? auditLogId = null, DateTime? startTime = null, DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, string entityTypeFullName = null, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
return Task.FromResult(0L); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<List<EntityChange>> GetListAsync(string sorting = null, int maxResultCount = 50, int skipCount = 0, Guid? auditLogId = null, DateTime? startTime = null, DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, string entityTypeFullName = null, bool includeDetails = false, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
return Task.FromResult(new List<EntityChange>()); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<EntityChangeWithUsername> GetWithUsernameAsync(Guid entityChangeId, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
EntityChangeWithUsername entityChange = null; |
||||||
|
return Task.FromResult(entityChange); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<List<EntityChangeWithUsername>> GetWithUsernameAsync(string entityId, string entityTypeFullName, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
return Task.FromResult(new List<EntityChangeWithUsername>()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[Dependency(TryRegister = true)] |
||||||
|
public class DefaultSecurityLogManager : ISecurityLogManager, ISingletonDependency |
||||||
|
{ |
||||||
|
public ILogger<DefaultSecurityLogManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public DefaultSecurityLogManager() |
||||||
|
{ |
||||||
|
Logger = NullLogger<DefaultSecurityLogManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
public Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No security log manager is available!"); |
||||||
|
return Task.FromResult(0L); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<List<SecurityLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No security log manager is available!"); |
||||||
|
return Task.FromResult(new List<SecurityLog>()); |
||||||
|
} |
||||||
|
|
||||||
|
public Task SaveAsync( |
||||||
|
SecurityLogInfo securityLogInfo, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No security log manager is available and is written to the local log by default"); |
||||||
|
Logger.LogInformation(securityLogInfo.ToString()); |
||||||
|
|
||||||
|
return Task.CompletedTask; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task<SecurityLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No security log manager is available!"); |
||||||
|
|
||||||
|
SecurityLog securityLog = null; |
||||||
|
return Task.FromResult(securityLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
Logger.LogDebug("No security log manager is available!"); |
||||||
|
return Task.CompletedTask; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.Data; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[DisableAuditing] |
||||||
|
public class EntityChange : IHasExtraProperties |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
|
||||||
|
public Guid AuditLogId { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public DateTime ChangeTime { get; set; } |
||||||
|
|
||||||
|
public EntityChangeType ChangeType { get; set; } |
||||||
|
|
||||||
|
public Guid? EntityTenantId { get; set; } |
||||||
|
|
||||||
|
public string EntityId { get; set; } |
||||||
|
|
||||||
|
public string EntityTypeFullName { get; set; } |
||||||
|
|
||||||
|
public List<EntityPropertyChange> PropertyChanges { get; set; } |
||||||
|
|
||||||
|
public ExtraPropertyDictionary ExtraProperties { get; set; } |
||||||
|
|
||||||
|
public EntityChange() |
||||||
|
{ |
||||||
|
PropertyChanges = new List<EntityPropertyChange>(); |
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
} |
||||||
|
|
||||||
|
public EntityChange( |
||||||
|
IGuidGenerator guidGenerator, |
||||||
|
Guid auditLogId, |
||||||
|
EntityChangeInfo entityChangeInfo, |
||||||
|
Guid? tenantId = null) |
||||||
|
{ |
||||||
|
Id = guidGenerator.Create(); |
||||||
|
AuditLogId = auditLogId; |
||||||
|
TenantId = tenantId; |
||||||
|
ChangeTime = entityChangeInfo.ChangeTime; |
||||||
|
ChangeType = entityChangeInfo.ChangeType; |
||||||
|
EntityId = entityChangeInfo.EntityId; |
||||||
|
EntityTypeFullName = entityChangeInfo.EntityTypeFullName; |
||||||
|
|
||||||
|
PropertyChanges = entityChangeInfo |
||||||
|
.PropertyChanges? |
||||||
|
.Select(p => new EntityPropertyChange(guidGenerator, Id, p, tenantId)) |
||||||
|
.ToList() |
||||||
|
?? new List<EntityPropertyChange>(); |
||||||
|
|
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
if (entityChangeInfo.ExtraProperties != null) |
||||||
|
{ |
||||||
|
foreach (var pair in entityChangeInfo.ExtraProperties) |
||||||
|
{ |
||||||
|
ExtraProperties.Add(pair.Key, pair.Value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
public class EntityChangeWithUsername |
||||||
|
{ |
||||||
|
public EntityChange EntityChange { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
using Volo.Abp.Guids; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[DisableAuditing] |
||||||
|
public class EntityPropertyChange |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public Guid EntityChangeId { get; set; } |
||||||
|
|
||||||
|
public string NewValue { get; set; } |
||||||
|
|
||||||
|
public string OriginalValue { get; set; } |
||||||
|
|
||||||
|
public string PropertyName { get; set; } |
||||||
|
|
||||||
|
public string PropertyTypeFullName { get; set; } |
||||||
|
|
||||||
|
public EntityPropertyChange() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public EntityPropertyChange( |
||||||
|
IGuidGenerator guidGenerator, |
||||||
|
Guid entityChangeId, |
||||||
|
EntityPropertyChangeInfo entityChangeInfo, |
||||||
|
Guid? tenantId = null) |
||||||
|
{ |
||||||
|
Id = guidGenerator.Create(); |
||||||
|
TenantId = tenantId; |
||||||
|
EntityChangeId = entityChangeId; |
||||||
|
NewValue = entityChangeInfo.NewValue; |
||||||
|
OriginalValue = entityChangeInfo.OriginalValue; |
||||||
|
PropertyName = entityChangeInfo.PropertyName; |
||||||
|
PropertyTypeFullName = entityChangeInfo.PropertyTypeFullName; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Net; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
public interface IAuditLogManager |
||||||
|
{ |
||||||
|
Task<AuditLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task DeleteAsync( |
||||||
|
Guid id, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<string> SaveAsync( |
||||||
|
AuditLogInfo auditInfo, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<List<AuditLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string httpMethod = null, |
||||||
|
string url = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string applicationName = null, |
||||||
|
string correlationId = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
int? maxExecutionDuration = null, |
||||||
|
int? minExecutionDuration = null, |
||||||
|
bool? hasException = null, |
||||||
|
HttpStatusCode? httpStatusCode = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
public interface IEntityChangeStore |
||||||
|
{ |
||||||
|
Task<EntityChange> GetAsync( |
||||||
|
Guid entityChangeId, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<long> GetCountAsync( |
||||||
|
Guid? auditLogId = null, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
EntityChangeType? changeType = null, |
||||||
|
string entityId = null, |
||||||
|
string entityTypeFullName = null, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<List<EntityChange>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
Guid? auditLogId = null, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
EntityChangeType? changeType = null, |
||||||
|
string entityId = null, |
||||||
|
string entityTypeFullName = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<EntityChangeWithUsername> GetWithUsernameAsync( |
||||||
|
Guid entityChangeId, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<List<EntityChangeWithUsername>> GetWithUsernameAsync( |
||||||
|
string entityId, |
||||||
|
string entityTypeFullName, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
public interface ISecurityLogManager |
||||||
|
{ |
||||||
|
Task<SecurityLog> GetAsync( |
||||||
|
Guid id, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task DeleteAsync( |
||||||
|
Guid id, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task SaveAsync( |
||||||
|
SecurityLogInfo securityLogInfo, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
Task<List<SecurityLog>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
|
||||||
|
Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
string applicationName = null, |
||||||
|
string identity = null, |
||||||
|
string action = null, |
||||||
|
Guid? userId = null, |
||||||
|
string userName = null, |
||||||
|
string clientId = null, |
||||||
|
string clientIpAddress = null, |
||||||
|
string correlationId = null, |
||||||
|
CancellationToken cancellationToken = default); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Data; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
public class SecurityLog : IHasExtraProperties |
||||||
|
{ |
||||||
|
public Guid Id { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public string ApplicationName { get; set; } |
||||||
|
|
||||||
|
public string Identity { get; set; } |
||||||
|
|
||||||
|
public string Action { get; set; } |
||||||
|
|
||||||
|
public Guid? UserId { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
|
||||||
|
public string TenantName { get; set; } |
||||||
|
|
||||||
|
public string ClientId { get; set; } |
||||||
|
|
||||||
|
public string CorrelationId { get; set; } |
||||||
|
|
||||||
|
public string ClientIpAddress { get; set; } |
||||||
|
|
||||||
|
public string BrowserInfo { get; set; } |
||||||
|
|
||||||
|
public DateTime CreationTime { get; set; } |
||||||
|
|
||||||
|
public ExtraPropertyDictionary ExtraProperties { get; set; } |
||||||
|
|
||||||
|
public SecurityLog() |
||||||
|
{ |
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
} |
||||||
|
|
||||||
|
public SecurityLog(Guid id, SecurityLogInfo securityLogInfo) |
||||||
|
{ |
||||||
|
Id = id; |
||||||
|
TenantId = securityLogInfo.TenantId; |
||||||
|
TenantName = securityLogInfo.TenantName; |
||||||
|
|
||||||
|
ApplicationName = securityLogInfo.ApplicationName; |
||||||
|
Identity = securityLogInfo.Identity; |
||||||
|
Action = securityLogInfo.Action; |
||||||
|
|
||||||
|
UserId = securityLogInfo.UserId; |
||||||
|
UserName = securityLogInfo.UserName; |
||||||
|
|
||||||
|
CreationTime = securityLogInfo.CreationTime; |
||||||
|
|
||||||
|
ClientIpAddress = securityLogInfo.ClientIpAddress; |
||||||
|
ClientId = securityLogInfo.ClientId; |
||||||
|
CorrelationId = securityLogInfo.CorrelationId; |
||||||
|
BrowserInfo = securityLogInfo.BrowserInfo; |
||||||
|
|
||||||
|
ExtraProperties = new ExtraPropertyDictionary(); |
||||||
|
if (securityLogInfo.ExtraProperties != null) |
||||||
|
{ |
||||||
|
foreach (var pair in securityLogInfo.ExtraProperties) |
||||||
|
{ |
||||||
|
ExtraProperties.Add(pair.Key, pair.Value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.SecurityLog; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class SecurityLogStore : ISecurityLogStore, ITransientDependency |
||||||
|
{ |
||||||
|
private readonly ISecurityLogManager _manager; |
||||||
|
|
||||||
|
public SecurityLogStore( |
||||||
|
ISecurityLogManager manager) |
||||||
|
{ |
||||||
|
_manager = manager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task SaveAsync(SecurityLogInfo securityLogInfo) |
||||||
|
{ |
||||||
|
await _manager.SaveAsync(securityLogInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,28 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<None Remove="Sanhe\Abp\Auditing\Localization\Resources\en.json" /> |
||||||
|
<None Remove="Sanhe\Abp\Auditing\Localization\Resources\zh-Hans.json" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<EmbeddedResource Include="Sanhe\Abp\Auditing\Localization\Resources\en.json" /> |
||||||
|
<EmbeddedResource Include="Sanhe\Abp\Auditing\Localization\Resources\zh-Hans.json" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.Features" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.AuditLogging.Domain.Shared" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,34 @@ |
|||||||
|
using Volo.Abp.Application; |
||||||
|
using Volo.Abp.AuditLogging; |
||||||
|
using Volo.Abp.AuditLogging.Localization; |
||||||
|
using Volo.Abp.Authorization; |
||||||
|
using Volo.Abp.Features; |
||||||
|
using Volo.Abp.Localization; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
using Volo.Abp.VirtualFileSystem; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpFeaturesModule), |
||||||
|
typeof(AbpAuthorizationModule), |
||||||
|
typeof(AbpAuditLoggingDomainSharedModule), |
||||||
|
typeof(AbpDddApplicationContractsModule))] |
||||||
|
public class AbpAuditingApplicationContractsModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||||
|
{ |
||||||
|
options.FileSets.AddEmbedded<AbpAuditingApplicationContractsModule>(); |
||||||
|
}); |
||||||
|
|
||||||
|
Configure<AbpLocalizationOptions>(options => |
||||||
|
{ |
||||||
|
options.Resources |
||||||
|
.Get<AuditLoggingResource>() |
||||||
|
.AddVirtualJson("/Sanhe/Abp/Auditing/Localization/Resources"); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public class AuditLogActionDto : ExtensibleEntityDto<Guid> |
||||||
|
{ |
||||||
|
public string ServiceName { get; set; } |
||||||
|
|
||||||
|
public string MethodName { get; set; } |
||||||
|
|
||||||
|
public string Parameters { get; set; } |
||||||
|
|
||||||
|
public DateTime ExecutionTime { get; set; } |
||||||
|
|
||||||
|
public int ExecutionDuration { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public class AuditLogDto : ExtensibleEntityDto<Guid> |
||||||
|
{ |
||||||
|
public string ApplicationName { get; set; } |
||||||
|
|
||||||
|
public Guid? UserId { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
|
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
public string TenantName { get; set; } |
||||||
|
|
||||||
|
public Guid? ImpersonatorUserId { get; set; } |
||||||
|
|
||||||
|
public Guid? ImpersonatorTenantId { get; set; } |
||||||
|
|
||||||
|
public DateTime ExecutionTime { get; set; } |
||||||
|
|
||||||
|
public int ExecutionDuration { get; set; } |
||||||
|
|
||||||
|
public string ClientIpAddress { get; set; } |
||||||
|
|
||||||
|
public string ClientName { get; set; } |
||||||
|
|
||||||
|
public string ClientId { get; set; } |
||||||
|
|
||||||
|
public string CorrelationId { get; set; } |
||||||
|
|
||||||
|
public string BrowserInfo { get; set; } |
||||||
|
|
||||||
|
public string HttpMethod { get; set; } |
||||||
|
|
||||||
|
public string Url { get; set; } |
||||||
|
|
||||||
|
public string Exceptions { get; set; } |
||||||
|
|
||||||
|
public string Comments { get; set; } |
||||||
|
|
||||||
|
public int? HttpStatusCode { get; set; } |
||||||
|
public List<EntityChangeDto> EntityChanges { get; set; } |
||||||
|
public List<AuditLogActionDto> Actions { get; set; } |
||||||
|
|
||||||
|
public AuditLogDto() |
||||||
|
{ |
||||||
|
EntityChanges = new List<EntityChangeDto>(); |
||||||
|
Actions = new List<AuditLogActionDto>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
using System; |
||||||
|
using System.Net; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public class AuditLogGetByPagedDto : PagedAndSortedResultRequestDto |
||||||
|
{ |
||||||
|
public DateTime? StartTime { get; set; } |
||||||
|
public DateTime? EndTime { get; set; } |
||||||
|
public string HttpMethod { get; set; } |
||||||
|
public string Url { get; set; } |
||||||
|
public Guid? UserId { get; set; } |
||||||
|
public string UserName { get; set; } |
||||||
|
public string ApplicationName { get; set; } |
||||||
|
public string CorrelationId { get; set; } |
||||||
|
public string ClientId { get; set; } |
||||||
|
public string ClientIpAddress { get; set; } |
||||||
|
public int? MaxExecutionDuration { get; set; } |
||||||
|
public int? MinExecutionDuration { get; set; } |
||||||
|
public bool? HasException { get; set; } |
||||||
|
public HttpStatusCode? HttpStatusCode { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public class EntityChangeDto : ExtensibleEntityDto<Guid> |
||||||
|
{ |
||||||
|
public DateTime ChangeTime { get; set; } |
||||||
|
|
||||||
|
public EntityChangeType ChangeType { get; set; } |
||||||
|
|
||||||
|
public Guid? EntityTenantId { get; set; } |
||||||
|
|
||||||
|
public string EntityId { get; set; } |
||||||
|
|
||||||
|
public string EntityTypeFullName { get; set; } |
||||||
|
|
||||||
|
public List<EntityPropertyChangeDto> PropertyChanges { get; set; } |
||||||
|
|
||||||
|
public EntityChangeDto() |
||||||
|
{ |
||||||
|
PropertyChanges = new List<EntityPropertyChangeDto>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Auditing; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
public class EntityChangeGetByPagedDto : PagedAndSortedResultRequestDto |
||||||
|
{ |
||||||
|
public Guid? AuditLogId { get; set; } |
||||||
|
public DateTime? StartTime { get; set; } |
||||||
|
public DateTime? EndTime { get; set; } |
||||||
|
public EntityChangeType? ChangeType { get; set; } |
||||||
|
public string EntityId { get; set; } |
||||||
|
public string EntityTypeFullName { get; set; } |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
public class EntityChangeGetWithUsernameDto |
||||||
|
{ |
||||||
|
public string EntityId { get; set; } |
||||||
|
public string EntityTypeFullName { get; set; } |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
public class EntityChangeWithUsernameDto |
||||||
|
{ |
||||||
|
public EntityChangeDto EntityChange { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public class EntityPropertyChangeDto : EntityDto<Guid> |
||||||
|
{ |
||||||
|
public string NewValue { get; set; } |
||||||
|
|
||||||
|
public string OriginalValue { get; set; } |
||||||
|
|
||||||
|
public string PropertyName { get; set; } |
||||||
|
|
||||||
|
public string PropertyTypeFullName { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Application.Services; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
public interface IAuditLogAppService : IApplicationService |
||||||
|
{ |
||||||
|
Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogGetByPagedDto input); |
||||||
|
|
||||||
|
Task<AuditLogDto> GetAsync(Guid id); |
||||||
|
|
||||||
|
Task DeleteAsync(Guid id); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Application.Services; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
public interface IEntityChangesAppService : IApplicationService |
||||||
|
{ |
||||||
|
Task<EntityChangeDto> GetAsync(Guid id); |
||||||
|
|
||||||
|
Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id); |
||||||
|
|
||||||
|
Task<PagedResultDto<EntityChangeDto>> GetListAsync(EntityChangeGetByPagedDto input); |
||||||
|
|
||||||
|
Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
public static class AuditingRemoteServiceConsts |
||||||
|
{ |
||||||
|
public const string RemoteServiceName = "AbpAuditing"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
using Volo.Abp.AuditLogging.Localization; |
||||||
|
using Volo.Abp.Features; |
||||||
|
using Volo.Abp.Localization; |
||||||
|
using Volo.Abp.Validation.StringValues; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Features |
||||||
|
{ |
||||||
|
public class AuditingFeatureDefinitionProvider : FeatureDefinitionProvider |
||||||
|
{ |
||||||
|
public override void Define(IFeatureDefinitionContext context) |
||||||
|
{ |
||||||
|
var auditingGroup = context.AddGroup( |
||||||
|
name: AuditingFeatureNames.GroupName, |
||||||
|
displayName: L("Features:Auditing")); |
||||||
|
|
||||||
|
var loggingFeature = auditingGroup.AddFeature( |
||||||
|
name: AuditingFeatureNames.Logging.Default, |
||||||
|
displayName: L("Features:Auditing"), |
||||||
|
description: L("Features:Auditing") |
||||||
|
); |
||||||
|
|
||||||
|
loggingFeature.CreateChild( |
||||||
|
name: AuditingFeatureNames.Logging.AuditLog, |
||||||
|
defaultValue: true.ToString(), |
||||||
|
displayName: L("Features:DisplayName:AuditLog"), |
||||||
|
description: L("Features:Description:AuditLog"), |
||||||
|
valueType: new ToggleStringValueType(new BooleanValueValidator()) |
||||||
|
); |
||||||
|
loggingFeature.CreateChild( |
||||||
|
name: AuditingFeatureNames.Logging.SecurityLog, |
||||||
|
defaultValue: true.ToString(), |
||||||
|
displayName: L("Features:DisplayName:SecurityLog"), |
||||||
|
description: L("Features:Description:SecurityLog"), |
||||||
|
valueType: new ToggleStringValueType(new BooleanValueValidator()) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
protected LocalizableString L(string name) |
||||||
|
{ |
||||||
|
return LocalizableString.Create<AuditLoggingResource>(name); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.Features |
||||||
|
{ |
||||||
|
public static class AuditingFeatureNames |
||||||
|
{ |
||||||
|
public const string GroupName = "AbpAuditing"; |
||||||
|
public class Logging |
||||||
|
{ |
||||||
|
public const string Default = GroupName + ".Logging"; |
||||||
|
|
||||||
|
public const string AuditLog = Default + ".AuditLog"; |
||||||
|
|
||||||
|
public const string SecurityLog = Default + ".SecurityLog"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
{ |
||||||
|
"culture": "en", |
||||||
|
"texts": { |
||||||
|
"Permissions:Auditing": "Auditing", |
||||||
|
"Permissions:AuditLog": "Audit log", |
||||||
|
"Permissions:SecurityLog": "Security log", |
||||||
|
"Permissions:DeleteLog": "Delete", |
||||||
|
"Features:Auditing": "Auditing", |
||||||
|
"Features:DisplayName:AuditLog": "Audit log", |
||||||
|
"Features:Description:AuditLog": "Whether to enable audit logging", |
||||||
|
"Features:DisplayName:SecurityLog": "Security log", |
||||||
|
"Features:Description:SecurityLog": "Whether to enable security logging", |
||||||
|
"SecurityLog": "Security log", |
||||||
|
"AuditLog": "Audit log", |
||||||
|
"Logging": "System Log", |
||||||
|
"Application": "Application", |
||||||
|
"ApplicationName": "Application name", |
||||||
|
"TenantId": "Tenant id", |
||||||
|
"TenantName": "Tenant name", |
||||||
|
"ImpersonatorTenantId": "Impersonator tenant", |
||||||
|
"UserInfo": "User info", |
||||||
|
"UserId": "User id", |
||||||
|
"UserName": "User name", |
||||||
|
"ImpersonatorUserId": "Impersonator user", |
||||||
|
"ClientId": "Client id", |
||||||
|
"ClientName": "Client name", |
||||||
|
"ClientIpAddress": "Ip address", |
||||||
|
"BrowserInfo": "Browser", |
||||||
|
"Operation": "Operation", |
||||||
|
"HttpMethod": "Http method", |
||||||
|
"RequestUrl": "Request url", |
||||||
|
"HttpStatusCode": "StatusCode", |
||||||
|
"HasException": "Has exception", |
||||||
|
"ExecutionTime": "Execution time", |
||||||
|
"MinExecutionDuration": "Min execution duration(ms)", |
||||||
|
"MaxExecutionDuration": "Max execution duration(ms)", |
||||||
|
"ExecutionDuration": "Execution duration(ms)", |
||||||
|
"Identity": "Identity", |
||||||
|
"ActionName": "Action", |
||||||
|
"CreationTime": "Creation time", |
||||||
|
"Additional": "Additional", |
||||||
|
"CorrelationId": "Correlation", |
||||||
|
"StartTime": "Start time", |
||||||
|
"EndTime": "End time", |
||||||
|
"SelectDateTime": "Select time", |
||||||
|
"InvokeMethod": "Invoke method", |
||||||
|
"ServiceName": "Service", |
||||||
|
"MethodName": "Method", |
||||||
|
"Parameters": "Parameters", |
||||||
|
"EntitiesChanged": "Entities changed", |
||||||
|
"ChangeType": "Change type", |
||||||
|
"EntityTypeFullName": "Entity type", |
||||||
|
"EntityId": "Entity id", |
||||||
|
"PropertyChanges": "Property changes", |
||||||
|
"PropertyName": "Property name", |
||||||
|
"NewValue": "New value", |
||||||
|
"OriginalValue": "Original value", |
||||||
|
"PropertyTypeFullName": "Property type", |
||||||
|
"Exception": "Exception", |
||||||
|
"StackTrack": "Stack track", |
||||||
|
"SecrchLog": "Secrch", |
||||||
|
"Created": "Created", |
||||||
|
"Updated": "Updated", |
||||||
|
"Deleted": "Deleted", |
||||||
|
"ShowLogDialog": "Show log", |
||||||
|
"DeleteLog": "Delete log", |
||||||
|
"MachineName": "Machine", |
||||||
|
"Environment": "Environment", |
||||||
|
"Context": "Context", |
||||||
|
"RequestId": "Request Id", |
||||||
|
"RequestPath": "Request Path", |
||||||
|
"ProcessId": "Process Id", |
||||||
|
"ThreadId": "Thread Id", |
||||||
|
"ActionId": "Action Id", |
||||||
|
"ConnectionId": "Connection Id", |
||||||
|
"Depth": "Depth", |
||||||
|
"Class": "Class", |
||||||
|
"Message": "Message", |
||||||
|
"Source": "Source", |
||||||
|
"StackTrace": "Stack Trace", |
||||||
|
"HResult": "HResult", |
||||||
|
"HelpURL": "Help Url", |
||||||
|
"TimeStamp": "Timestamp", |
||||||
|
"Level": "Level", |
||||||
|
"Fields": "Fields", |
||||||
|
"Exceptions": "Exceptions" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
{ |
||||||
|
"culture": "zh-Hans", |
||||||
|
"texts": { |
||||||
|
"Permissions:Auditing": "内部审计", |
||||||
|
"Permissions:AuditLog": "审计日志", |
||||||
|
"Permissions:SecurityLog": "安全日志", |
||||||
|
"Permissions:DeleteLog": "删除日志", |
||||||
|
"Features:Auditing": "内部审计", |
||||||
|
"Features:DisplayName:AuditLog": "审计日志", |
||||||
|
"Features:Description:AuditLog": "是否启用审计日志功能", |
||||||
|
"Features:DisplayName:SecurityLog": "安全日志", |
||||||
|
"Features:Description:SecurityLog": "是否启用安全日志功能", |
||||||
|
"SecurityLog": "安全日志", |
||||||
|
"AuditLog": "审计日志", |
||||||
|
"Logging": "系统日志", |
||||||
|
"Application": "应用信息", |
||||||
|
"ApplicationName": "应用名称", |
||||||
|
"TenantId": "租户标识", |
||||||
|
"TenantName": "租户名称", |
||||||
|
"ImpersonatorTenantId": "模拟租户", |
||||||
|
"UserInfo": "用户信息", |
||||||
|
"UserId": "用户标识", |
||||||
|
"UserName": "用户名称", |
||||||
|
"ImpersonatorUserId": "模拟用户", |
||||||
|
"ClientId": "客户端标识", |
||||||
|
"ClientName": "客户端名称", |
||||||
|
"ClientIpAddress": "客户端地址", |
||||||
|
"BrowserInfo": "浏览器信息", |
||||||
|
"Operation": "操作信息", |
||||||
|
"HttpMethod": "请求方法", |
||||||
|
"RequestUrl": "请求路径", |
||||||
|
"HttpStatusCode": "响应状态", |
||||||
|
"HasException": "包含异常", |
||||||
|
"ExecutionTime": "调用时间", |
||||||
|
"MinExecutionDuration": "最短响应时间(ms)", |
||||||
|
"MaxExecutionDuration": "最长响应时间(ms)", |
||||||
|
"ExecutionDuration": "响应时间(ms)", |
||||||
|
"Identity": "主体名称", |
||||||
|
"ActionName": "方法名称", |
||||||
|
"CreationTime": "创建时间", |
||||||
|
"Additional": "附加信息", |
||||||
|
"CorrelationId": "链路标识", |
||||||
|
"StartTime": "开始时间", |
||||||
|
"EndTime": "结束时间", |
||||||
|
"SelectDateTime": "选择日期时间", |
||||||
|
"InvokeMethod": "调用方法", |
||||||
|
"ServiceName": "服务名称", |
||||||
|
"MethodName": "方法名称", |
||||||
|
"Parameters": "参数列表", |
||||||
|
"EntitiesChanged": "实体变更", |
||||||
|
"ChangeType": "变更类型", |
||||||
|
"EntityTypeFullName": "实体类型", |
||||||
|
"EntityId": "实体标识", |
||||||
|
"PropertyChanges": "属性变更", |
||||||
|
"PropertyName": "属性名称", |
||||||
|
"NewValue": "当前值", |
||||||
|
"OriginalValue": "原始值", |
||||||
|
"PropertyTypeFullName": "属性类型", |
||||||
|
"Exception": "异常信息", |
||||||
|
"StackTrack": "异常堆栈", |
||||||
|
"SecrchLog": "查询日志", |
||||||
|
"Created": "新增", |
||||||
|
"Updated": "修改", |
||||||
|
"Deleted": "删除", |
||||||
|
"ShowLogDialog": "查看日志", |
||||||
|
"DeleteLog": "删除日志", |
||||||
|
"MachineName": "机器名称", |
||||||
|
"Environment": "应用环境", |
||||||
|
"Context": "上下文", |
||||||
|
"RequestId": "请求标识", |
||||||
|
"RequestPath": "请求路径", |
||||||
|
"ProcessId": "进程标识", |
||||||
|
"ThreadId": "线程标识", |
||||||
|
"ActionId": "方法标识", |
||||||
|
"ConnectionId": "连接标识", |
||||||
|
"Depth": "深度", |
||||||
|
"Class": "类型", |
||||||
|
"Message": "信息", |
||||||
|
"Source": "来源", |
||||||
|
"StackTrace": "堆栈", |
||||||
|
"HResult": "代码", |
||||||
|
"HelpURL": "帮助", |
||||||
|
"TimeStamp": "时间戳", |
||||||
|
"Level": "级别", |
||||||
|
"Fields": "字段", |
||||||
|
"Exceptions": "错误信息" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Logging.Dto |
||||||
|
{ |
||||||
|
public class LogDto |
||||||
|
{ |
||||||
|
public DateTime TimeStamp { get; set; } |
||||||
|
public LogLevel Level { get; set; } |
||||||
|
public string Message { get; set; } |
||||||
|
public LogFieldDto Fields { get; set; } |
||||||
|
public List<LogExceptionDto> Exceptions { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.Logging.Dto |
||||||
|
{ |
||||||
|
public class LogExceptionDto |
||||||
|
{ |
||||||
|
public int Depth { get; set; } |
||||||
|
public string Class { get; set; } |
||||||
|
public string Message { get; set; } |
||||||
|
public string Source { get; set; } |
||||||
|
public string StackTrace { get; set; } |
||||||
|
public int HResult { get; set; } |
||||||
|
public string HelpURL { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.Logging.Dto |
||||||
|
{ |
||||||
|
public class LogFieldDto |
||||||
|
{ |
||||||
|
public string Id { get; set; } |
||||||
|
public string MachineName { get; set; } |
||||||
|
public string Environment { get; set; } |
||||||
|
public string Application { get; set; } |
||||||
|
public string Context { get; set; } |
||||||
|
public string ActionId { get; set; } |
||||||
|
public string ActionName { get; set; } |
||||||
|
public string RequestId { get; set; } |
||||||
|
public string RequestPath { get; set; } |
||||||
|
public string ConnectionId { get; set; } |
||||||
|
public string CorrelationId { get; set; } |
||||||
|
public string ClientId { get; set; } |
||||||
|
public string UserId { get; set; } |
||||||
|
public int ProcessId { get; set; } |
||||||
|
public int ThreadId { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Logging.Dto |
||||||
|
{ |
||||||
|
public class LogGetByPagedDto : PagedAndSortedResultRequestDto |
||||||
|
{ |
||||||
|
public DateTime? StartTime { get; set; } |
||||||
|
public DateTime? EndTime { get; set; } |
||||||
|
public LogLevel? Level { get; set; } |
||||||
|
public string MachineName { get; set; } |
||||||
|
public string Environment { get; set; } |
||||||
|
public string Application { get; set; } |
||||||
|
public string Context { get; set; } |
||||||
|
public string RequestId { get; set; } |
||||||
|
public string RequestPath { get; set; } |
||||||
|
public string CorrelationId { get; set; } |
||||||
|
public int? ProcessId { get; set; } |
||||||
|
public int? ThreadId { get; set; } |
||||||
|
public bool? HasException { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
using Sanhe.Abp.Auditing.Logging.Dto; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Application.Services; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Logging |
||||||
|
{ |
||||||
|
public interface ILogAppService : IApplicationService |
||||||
|
{ |
||||||
|
Task<LogDto> GetAsync(string id); |
||||||
|
|
||||||
|
Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
using Volo.Abp.AuditLogging.Localization; |
||||||
|
using Volo.Abp.Authorization.Permissions; |
||||||
|
using Volo.Abp.Localization; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Permissions |
||||||
|
{ |
||||||
|
public class AuditingPermissionDefinitionProvider : PermissionDefinitionProvider |
||||||
|
{ |
||||||
|
public override void Define(IPermissionDefinitionContext context) |
||||||
|
{ |
||||||
|
var auditingGroup = context.AddGroup( |
||||||
|
name: AuditingPermissionNames.GroupName, |
||||||
|
displayName: L("Permissions:Auditing")); |
||||||
|
|
||||||
|
var auditLogPermission = auditingGroup.AddPermission( |
||||||
|
name: AuditingPermissionNames.AuditLog.Default, |
||||||
|
displayName: L("Permissions:AuditLog")); |
||||||
|
auditLogPermission.AddChild( |
||||||
|
name: AuditingPermissionNames.AuditLog.Delete, |
||||||
|
displayName: L("Permissions:DeleteLog")); |
||||||
|
|
||||||
|
var securityLogPermission = auditingGroup.AddPermission( |
||||||
|
name: AuditingPermissionNames.SecurityLog.Default, |
||||||
|
displayName: L("Permissions:SecurityLog")); |
||||||
|
securityLogPermission.AddChild( |
||||||
|
name: AuditingPermissionNames.SecurityLog.Delete, |
||||||
|
displayName: L("Permissions:DeleteLog")); |
||||||
|
} |
||||||
|
|
||||||
|
protected LocalizableString L(string name) |
||||||
|
{ |
||||||
|
return LocalizableString.Create<AuditLoggingResource>(name); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
namespace Sanhe.Abp.Auditing.Permissions |
||||||
|
{ |
||||||
|
public class AuditingPermissionNames |
||||||
|
{ |
||||||
|
public const string GroupName = "AbpAuditing"; |
||||||
|
public class AuditLog |
||||||
|
{ |
||||||
|
public const string Default = GroupName + ".AuditLog"; |
||||||
|
public const string Delete = Default + ".Delete"; |
||||||
|
} |
||||||
|
|
||||||
|
public class SecurityLog |
||||||
|
{ |
||||||
|
public const string Default = GroupName + ".SecurityLog"; |
||||||
|
public const string Delete = Default + ".Delete"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Application.Services; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.SecurityLogs |
||||||
|
{ |
||||||
|
public interface ISecurityLogAppService : IApplicationService |
||||||
|
{ |
||||||
|
Task<PagedResultDto<SecurityLogDto>> GetListAsync(SecurityLogGetByPagedDto input); |
||||||
|
|
||||||
|
Task<SecurityLogDto> GetAsync(Guid id); |
||||||
|
|
||||||
|
Task DeleteAsync(Guid id); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.SecurityLogs |
||||||
|
{ |
||||||
|
public class SecurityLogDto : ExtensibleEntityDto<Guid> |
||||||
|
{ |
||||||
|
public string ApplicationName { get; set; } |
||||||
|
|
||||||
|
public string Identity { get; set; } |
||||||
|
|
||||||
|
public string Action { get; set; } |
||||||
|
|
||||||
|
public Guid? UserId { get; set; } |
||||||
|
|
||||||
|
public string UserName { get; set; } |
||||||
|
|
||||||
|
public string TenantName { get; set; } |
||||||
|
|
||||||
|
public string ClientId { get; set; } |
||||||
|
|
||||||
|
public string CorrelationId { get; set; } |
||||||
|
|
||||||
|
public string ClientIpAddress { get; set; } |
||||||
|
|
||||||
|
public string BrowserInfo { get; set; } |
||||||
|
|
||||||
|
public DateTime CreationTime { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
using System; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.SecurityLogs |
||||||
|
{ |
||||||
|
public class SecurityLogGetByPagedDto : PagedAndSortedResultRequestDto |
||||||
|
{ |
||||||
|
public DateTime? StartTime { get; set; } |
||||||
|
public DateTime? EndTime { get; set; } |
||||||
|
public string ApplicationName { get; set; } |
||||||
|
public string Identity { get; set; } |
||||||
|
public string ActionName { get; set; } |
||||||
|
public Guid? UserId { get; set; } |
||||||
|
public string UserName { get; set; } |
||||||
|
public string ClientId { get; set; } |
||||||
|
public string CorrelationId { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,22 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\..\logging\Sanhe.Abp.Logging\Sanhe.Abp.Logging.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.Auditing.Application.Contracts\Sanhe.Abp.Auditing.Application.Contracts.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.AuditLogging\Sanhe.Abp.AuditLogging.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,24 @@ |
|||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Sanhe.Abp.AuditLogging; |
||||||
|
using Volo.Abp.AutoMapper; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpAutoMapperModule), |
||||||
|
typeof(AbpAuditLoggingModule), |
||||||
|
typeof(AbpAuditingApplicationContractsModule))] |
||||||
|
public class AbpAuditingApplicationModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
context.Services.AddAutoMapperObjectMapper<AbpAuditingApplicationModule>(); |
||||||
|
|
||||||
|
Configure<AbpAutoMapperOptions>(options => |
||||||
|
{ |
||||||
|
options.AddProfile<AbpAuditingMapperProfile>(validate: true); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
using AutoMapper; |
||||||
|
using Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
using Sanhe.Abp.Auditing.Logging.Dto; |
||||||
|
using Sanhe.Abp.Auditing.SecurityLogs; |
||||||
|
using Sanhe.Abp.AuditLogging; |
||||||
|
using Sanhe.Abp.Logging; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
public class AbpAuditingMapperProfile : Profile |
||||||
|
{ |
||||||
|
public AbpAuditingMapperProfile() |
||||||
|
{ |
||||||
|
CreateMap<AuditLogAction, AuditLogActionDto>() |
||||||
|
.MapExtraProperties(); |
||||||
|
CreateMap<EntityPropertyChange, EntityPropertyChangeDto>(); |
||||||
|
CreateMap<EntityChangeWithUsername, EntityChangeWithUsernameDto>(); |
||||||
|
CreateMap<EntityChange, EntityChangeDto>() |
||||||
|
.MapExtraProperties(); |
||||||
|
CreateMap<AuditLog, AuditLogDto>() |
||||||
|
.MapExtraProperties(); |
||||||
|
|
||||||
|
CreateMap<SecurityLog, SecurityLogDto>() |
||||||
|
.MapExtraProperties(); |
||||||
|
|
||||||
|
CreateMap<LogField, LogFieldDto>(); |
||||||
|
CreateMap<LogException, LogExceptionDto>(); |
||||||
|
CreateMap<LogInfo, LogDto>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Sanhe.Abp.Auditing.Features; |
||||||
|
using Sanhe.Abp.Auditing.Permissions; |
||||||
|
using Sanhe.Abp.AuditLogging; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.ComponentModel.DataAnnotations; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Features; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
[Authorize(AuditingPermissionNames.AuditLog.Default)] |
||||||
|
[RequiresFeature(AuditingFeatureNames.Logging.AuditLog)] |
||||||
|
public class AuditLogAppService : AuditingApplicationServiceBase, IAuditLogAppService |
||||||
|
{ |
||||||
|
protected IAuditLogManager AuditLogManager { get; } |
||||||
|
|
||||||
|
public AuditLogAppService(IAuditLogManager auditLogManager) |
||||||
|
{ |
||||||
|
AuditLogManager = auditLogManager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<AuditLogDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
var auditLog = await AuditLogManager.GetAsync(id, includeDetails: true); |
||||||
|
|
||||||
|
return ObjectMapper.Map<AuditLog, AuditLogDto>(auditLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogGetByPagedDto input) |
||||||
|
{ |
||||||
|
var auditLogCount = await AuditLogManager |
||||||
|
.GetCountAsync(input.StartTime, input.EndTime, |
||||||
|
input.HttpMethod, input.Url, |
||||||
|
input.UserId, input.UserName, |
||||||
|
input.ApplicationName, input.CorrelationId, |
||||||
|
input.ClientId, input.ClientIpAddress, |
||||||
|
input.MaxExecutionDuration, input.MinExecutionDuration, |
||||||
|
input.HasException, input.HttpStatusCode); |
||||||
|
|
||||||
|
var auditLogs = await AuditLogManager |
||||||
|
.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, |
||||||
|
input.StartTime, input.EndTime, |
||||||
|
input.HttpMethod, input.Url, |
||||||
|
input.UserId, input.UserName, |
||||||
|
input.ApplicationName, input.CorrelationId, |
||||||
|
input.ClientId, input.ClientIpAddress, |
||||||
|
input.MaxExecutionDuration, input.MinExecutionDuration, |
||||||
|
input.HasException, input.HttpStatusCode, includeDetails: false); |
||||||
|
|
||||||
|
return new PagedResultDto<AuditLogDto>(auditLogCount, |
||||||
|
ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(auditLogs)); |
||||||
|
} |
||||||
|
|
||||||
|
[Authorize(AuditingPermissionNames.AuditLog.Delete)] |
||||||
|
public virtual async Task DeleteAsync([Required] Guid id) |
||||||
|
{ |
||||||
|
await AuditLogManager.DeleteAsync(id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Sanhe.Abp.Auditing.Features; |
||||||
|
using Sanhe.Abp.Auditing.Permissions; |
||||||
|
using Sanhe.Abp.AuditLogging; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Features; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
[Authorize(AuditingPermissionNames.AuditLog.Default)] |
||||||
|
[RequiresFeature(AuditingFeatureNames.Logging.AuditLog)] |
||||||
|
public class EntityChangesAppService : AuditingApplicationServiceBase, IEntityChangesAppService |
||||||
|
{ |
||||||
|
protected IEntityChangeStore EntityChangeStore { get; } |
||||||
|
|
||||||
|
public EntityChangesAppService(IEntityChangeStore entityChangeStore) |
||||||
|
{ |
||||||
|
EntityChangeStore = entityChangeStore; |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<EntityChangeDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
var entityChange = await EntityChangeStore.GetAsync(id); |
||||||
|
|
||||||
|
return ObjectMapper.Map<EntityChange, EntityChangeDto>(entityChange); |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<PagedResultDto<EntityChangeDto>> GetListAsync(EntityChangeGetByPagedDto input) |
||||||
|
{ |
||||||
|
var totalCount = await EntityChangeStore.GetCountAsync( |
||||||
|
input.AuditLogId, input.StartTime, input.EndTime, |
||||||
|
input.ChangeType, input.EntityId, input.EntityTypeFullName); |
||||||
|
|
||||||
|
var entityChanges = await EntityChangeStore.GetListAsync( |
||||||
|
input.Sorting, input.MaxResultCount, input.SkipCount, |
||||||
|
input.AuditLogId, input.StartTime, input.EndTime, |
||||||
|
input.ChangeType, input.EntityId, input.EntityTypeFullName); |
||||||
|
|
||||||
|
return new PagedResultDto<EntityChangeDto>(totalCount, |
||||||
|
ObjectMapper.Map<List<EntityChange>, List<EntityChangeDto>>(entityChanges)); |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id) |
||||||
|
{ |
||||||
|
var entityChangeWithUsername = await EntityChangeStore.GetWithUsernameAsync(id); |
||||||
|
|
||||||
|
return ObjectMapper.Map<EntityChangeWithUsername, EntityChangeWithUsernameDto>(entityChangeWithUsername); |
||||||
|
} |
||||||
|
|
||||||
|
public async virtual Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) |
||||||
|
{ |
||||||
|
var entityChangeWithUsernames = await EntityChangeStore.GetWithUsernameAsync( |
||||||
|
input.EntityId, input.EntityTypeFullName); |
||||||
|
|
||||||
|
return new ListResultDto<EntityChangeWithUsernameDto>( |
||||||
|
ObjectMapper.Map<List<EntityChangeWithUsername>, List<EntityChangeWithUsernameDto>>(entityChangeWithUsernames)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
using Volo.Abp.Application.Services; |
||||||
|
using Volo.Abp.AuditLogging.Localization; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
public abstract class AuditingApplicationServiceBase : ApplicationService |
||||||
|
{ |
||||||
|
protected AuditingApplicationServiceBase() |
||||||
|
{ |
||||||
|
LocalizationResource = typeof(AuditLoggingResource); |
||||||
|
ObjectMapperContext = typeof(AbpAuditingApplicationModule); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
using Sanhe.Abp.Auditing.Logging.Dto; |
||||||
|
using Sanhe.Abp.Logging; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Logging |
||||||
|
{ |
||||||
|
public class LogAppService : AuditingApplicationServiceBase, ILogAppService |
||||||
|
{ |
||||||
|
private readonly ILoggingManager _manager; |
||||||
|
|
||||||
|
public LogAppService(ILoggingManager manager) |
||||||
|
{ |
||||||
|
_manager = manager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<LogDto> GetAsync(string id) |
||||||
|
{ |
||||||
|
var log = await _manager.GetAsync(id); |
||||||
|
|
||||||
|
return ObjectMapper.Map<LogInfo, LogDto>(log); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input) |
||||||
|
{ |
||||||
|
var count = await _manager.GetCountAsync( |
||||||
|
input.StartTime, input.EndTime, input.Level, |
||||||
|
input.MachineName, input.Environment, |
||||||
|
input.Application, input.Context, |
||||||
|
input.RequestId, input.RequestPath, |
||||||
|
input.CorrelationId, input.ProcessId, |
||||||
|
input.ThreadId, input.HasException); |
||||||
|
|
||||||
|
var logs = await _manager.GetListAsync( |
||||||
|
input.Sorting, input.MaxResultCount, input.SkipCount, |
||||||
|
input.StartTime, input.EndTime, input.Level, |
||||||
|
input.MachineName, input.Environment, |
||||||
|
input.Application, input.Context, |
||||||
|
input.RequestId, input.RequestPath, |
||||||
|
input.CorrelationId, input.ProcessId, |
||||||
|
input.ThreadId, input.HasException, |
||||||
|
includeDetails: false); |
||||||
|
|
||||||
|
return new PagedResultDto<LogDto>(count, |
||||||
|
ObjectMapper.Map<List<LogInfo>, List<LogDto>>(logs)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
using Microsoft.AspNetCore.Authorization; |
||||||
|
using Sanhe.Abp.Auditing.Features; |
||||||
|
using Sanhe.Abp.Auditing.Permissions; |
||||||
|
using Sanhe.Abp.AuditLogging; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.Features; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.SecurityLogs |
||||||
|
{ |
||||||
|
[Authorize(AuditingPermissionNames.SecurityLog.Default)] |
||||||
|
[RequiresFeature(AuditingFeatureNames.Logging.SecurityLog)] |
||||||
|
public class SecurityLogAppService : AuditingApplicationServiceBase, ISecurityLogAppService |
||||||
|
{ |
||||||
|
protected ISecurityLogManager SecurityLogManager { get; } |
||||||
|
|
||||||
|
public SecurityLogAppService(ISecurityLogManager securityLogManager) |
||||||
|
{ |
||||||
|
SecurityLogManager = securityLogManager; |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<SecurityLogDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
var securityLog = await SecurityLogManager.GetAsync(id, includeDetails: true); |
||||||
|
|
||||||
|
return ObjectMapper.Map<SecurityLog, SecurityLogDto>(securityLog); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<PagedResultDto<SecurityLogDto>> GetListAsync(SecurityLogGetByPagedDto input) |
||||||
|
{ |
||||||
|
var securityLogCount = await SecurityLogManager |
||||||
|
.GetCountAsync(input.StartTime, input.EndTime, |
||||||
|
input.ApplicationName, input.Identity, input.ActionName, |
||||||
|
input.UserId, input.UserName, input.ClientId, input.CorrelationId |
||||||
|
); |
||||||
|
|
||||||
|
var securityLogs = await SecurityLogManager |
||||||
|
.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, |
||||||
|
input.StartTime, input.EndTime, |
||||||
|
input.ApplicationName, input.Identity, input.ActionName, |
||||||
|
input.UserId, input.UserName, input.ClientId, input.CorrelationId, |
||||||
|
includeDetails: false |
||||||
|
); |
||||||
|
|
||||||
|
return new PagedResultDto<SecurityLogDto>(securityLogCount, |
||||||
|
ObjectMapper.Map<List<SecurityLog>, List<SecurityLogDto>>(securityLogs)); |
||||||
|
} |
||||||
|
|
||||||
|
[Authorize(AuditingPermissionNames.SecurityLog.Delete)] |
||||||
|
public virtual async Task DeleteAsync(Guid id) |
||||||
|
{ |
||||||
|
await SecurityLogManager.DeleteAsync(id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,19 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>net6.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.Auditing.Application.Contracts\Sanhe.Abp.Auditing.Application.Contracts.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,41 @@ |
|||||||
|
using Localization.Resources.AbpUi; |
||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||||
|
using Volo.Abp.AspNetCore.Mvc.Localization; |
||||||
|
using Volo.Abp.AuditLogging.Localization; |
||||||
|
using Volo.Abp.Localization; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpAspNetCoreMvcModule), |
||||||
|
typeof(AbpAuditingApplicationContractsModule))] |
||||||
|
public class AbpAuditingHttpApiModule : AbpModule |
||||||
|
{ |
||||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
PreConfigure<IMvcBuilder>(mvcBuilder => |
||||||
|
{ |
||||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAuditingHttpApiModule).Assembly); |
||||||
|
}); |
||||||
|
|
||||||
|
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
||||||
|
{ |
||||||
|
options.AddAssemblyResource(typeof(AuditLoggingResource), typeof(AbpAuditingApplicationContractsModule).Assembly); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
Configure<AbpLocalizationOptions>(options => |
||||||
|
{ |
||||||
|
options.Resources |
||||||
|
.Get<AuditLoggingResource>() |
||||||
|
.AddBaseTypes( |
||||||
|
typeof(AbpUiResource) |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 审计日志 |
||||||
|
/// </summary> |
||||||
|
[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] |
||||||
|
[Area("auditing")] |
||||||
|
[ControllerName("audit-log")] |
||||||
|
[Route("api/auditing/audit-log")] |
||||||
|
public class AuditLogController : AbpController, IAuditLogAppService |
||||||
|
{ |
||||||
|
protected IAuditLogAppService AuditLogAppService { get; } |
||||||
|
|
||||||
|
public AuditLogController(IAuditLogAppService auditLogAppService) |
||||||
|
{ |
||||||
|
AuditLogAppService = auditLogAppService; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 根据Id获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
[Route("{id}")] |
||||||
|
public virtual Task<AuditLogDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
return AuditLogAppService.GetAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 分页获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
public virtual Task<PagedResultDto<AuditLogDto>> GetListAsync(AuditLogGetByPagedDto input) |
||||||
|
{ |
||||||
|
return AuditLogAppService.GetListAsync(input); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 根据Id删除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpDelete] |
||||||
|
[Route("{id}")] |
||||||
|
public virtual Task DeleteAsync(Guid id) |
||||||
|
{ |
||||||
|
return AuditLogAppService.DeleteAsync(id); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.AuditLogs; |
||||||
|
|
||||||
|
[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] |
||||||
|
[Area("auditing")] |
||||||
|
[ControllerName("entity-changes")] |
||||||
|
[Route("api/auditing/entity-changes")] |
||||||
|
public class EntityChangesController : AbpControllerBase, IEntityChangesAppService |
||||||
|
{ |
||||||
|
protected IEntityChangesAppService EntityChangeAppService { get; } |
||||||
|
|
||||||
|
public EntityChangesController(IEntityChangesAppService entityChangeAppService) |
||||||
|
{ |
||||||
|
EntityChangeAppService = entityChangeAppService; |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet] |
||||||
|
[Route("{id}")] |
||||||
|
public Task<EntityChangeDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
return EntityChangeAppService.GetAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet] |
||||||
|
public Task<PagedResultDto<EntityChangeDto>> GetListAsync(EntityChangeGetByPagedDto input) |
||||||
|
{ |
||||||
|
return EntityChangeAppService.GetListAsync(input); |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet] |
||||||
|
[Route("with-username/{id}")] |
||||||
|
public Task<EntityChangeWithUsernameDto> GetWithUsernameAsync(Guid id) |
||||||
|
{ |
||||||
|
return EntityChangeAppService.GetWithUsernameAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
[HttpGet] |
||||||
|
[Route("with-username")] |
||||||
|
public Task<ListResultDto<EntityChangeWithUsernameDto>> GetWithUsernameAsync(EntityChangeGetWithUsernameDto input) |
||||||
|
{ |
||||||
|
return EntityChangeAppService.GetWithUsernameAsync(input); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using Sanhe.Abp.Auditing.Logging.Dto; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.Logging |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 日志 |
||||||
|
/// </summary> |
||||||
|
[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] |
||||||
|
[Area("auditing")] |
||||||
|
[ControllerName("logging")] |
||||||
|
[Route("api/auditing/logging")] |
||||||
|
public class LogController : AbpController, ILogAppService |
||||||
|
{ |
||||||
|
private readonly ILogAppService _service; |
||||||
|
|
||||||
|
public LogController(ILogAppService service) |
||||||
|
{ |
||||||
|
_service = service; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 根据Id获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
[Route("{id}")] |
||||||
|
public virtual Task<LogDto> GetAsync(string id) |
||||||
|
{ |
||||||
|
return _service.GetAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 分页获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
public virtual Task<PagedResultDto<LogDto>> GetListAsync(LogGetByPagedDto input) |
||||||
|
{ |
||||||
|
return _service.GetListAsync(input); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
using Microsoft.AspNetCore.Mvc; |
||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp; |
||||||
|
using Volo.Abp.Application.Dtos; |
||||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Auditing.SecurityLogs |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// 安全日志 |
||||||
|
/// </summary> |
||||||
|
[RemoteService(Name = AuditingRemoteServiceConsts.RemoteServiceName)] |
||||||
|
[Area("auditing")] |
||||||
|
[ControllerName("security-log")] |
||||||
|
[Route("api/auditing/security-log")] |
||||||
|
public class SecurityLogController : AbpController, ISecurityLogAppService |
||||||
|
{ |
||||||
|
protected ISecurityLogAppService SecurityLogAppService { get; } |
||||||
|
|
||||||
|
public SecurityLogController(ISecurityLogAppService securityLogAppService) |
||||||
|
{ |
||||||
|
SecurityLogAppService = securityLogAppService; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 根据Id获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
[Route("{id}")] |
||||||
|
public virtual Task<SecurityLogDto> GetAsync(Guid id) |
||||||
|
{ |
||||||
|
return SecurityLogAppService.GetAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 分页获取 |
||||||
|
/// </summary> |
||||||
|
/// <param name="input"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpGet] |
||||||
|
public virtual Task<PagedResultDto<SecurityLogDto>> GetListAsync(SecurityLogGetByPagedDto input) |
||||||
|
{ |
||||||
|
return SecurityLogAppService.GetListAsync(input); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 根据Id删除 |
||||||
|
/// </summary> |
||||||
|
/// <param name="id"></param> |
||||||
|
/// <returns></returns> |
||||||
|
[HttpDelete] |
||||||
|
[Route("{id}")] |
||||||
|
public virtual Task DeleteAsync(Guid id) |
||||||
|
{ |
||||||
|
return SecurityLogAppService.DeleteAsync(id); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,32 @@ |
|||||||
|
# LINGYUN.Abp.Logging.Serilog.Elasticsearch |
||||||
|
|
||||||
|
ILoggingManager 接口的ES实现, 从ES中检索日志信息 |
||||||
|
|
||||||
|
## 模块引用 |
||||||
|
|
||||||
|
```csharp |
||||||
|
[DependsOn(typeof(AbpLoggingSerilogElasticsearchModule))] |
||||||
|
public class YouProjectModule : AbpModule |
||||||
|
{ |
||||||
|
// other |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 配置项 |
||||||
|
|
||||||
|
* AbpLoggingSerilogElasticsearchOptions.IndexFormat 必须和Serilog配置项中的IndexFormat相同,否则无法定位到正确的索引 |
||||||
|
|
||||||
|
## appsettings.json |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"Logging": { |
||||||
|
"Serilog": { |
||||||
|
"Elasticsearch": { |
||||||
|
"IndexFormat": "logstash-{0:yyyy.MM.dd}" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
``` |
@ -0,0 +1,24 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="$(SerilogSinksElasticsearchPackageVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.AutoMapper" Version="$(VoloAbpVersion)" /> |
||||||
|
<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\..\elasticsearch\Sanhe.Abp.Elasticsearch\Sanhe.Abp.Elasticsearch.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.Logging\Sanhe.Abp.Logging.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.Serilog.Enrichers.Application\Sanhe.Abp.Serilog.Enrichers.Application.csproj" /> |
||||||
|
<ProjectReference Include="..\Sanhe.Abp.Serilog.Enrichers.UniqueId\Sanhe.Abp.Serilog.Enrichers.UniqueId.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,16 @@ |
|||||||
|
using AutoMapper; |
||||||
|
using Sanhe.Abp.Logging; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
public class AbpLoggingSerilogElasticsearchMapperProfile : Profile |
||||||
|
{ |
||||||
|
public AbpLoggingSerilogElasticsearchMapperProfile() |
||||||
|
{ |
||||||
|
CreateMap<SerilogException, LogException>(); |
||||||
|
CreateMap<SerilogField, LogField>() |
||||||
|
.ForMember(log => log.Id, map => map.MapFrom(slog => slog.UniqueId.ToString())); |
||||||
|
CreateMap<SerilogInfo, LogInfo>(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Volo.Abp.AutoMapper; |
||||||
|
using Volo.Abp.Json; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
using Sanhe.Abp.Logging; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
[DependsOn( |
||||||
|
typeof(AbpLoggingModule), |
||||||
|
typeof(AbpElasticsearchModule), |
||||||
|
typeof(AbpAutoMapperModule), |
||||||
|
typeof(AbpJsonModule))] |
||||||
|
public class AbpLoggingSerilogElasticsearchModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
var configuration = context.Services.GetConfiguration(); |
||||||
|
|
||||||
|
Configure<AbpLoggingSerilogElasticsearchOptions>(configuration.GetSection("Logging:Serilog:Elasticsearch")); |
||||||
|
|
||||||
|
context.Services.AddAutoMapperObjectMapper<AbpLoggingSerilogElasticsearchModule>(); |
||||||
|
|
||||||
|
Configure<AbpAutoMapperOptions>(options => |
||||||
|
{ |
||||||
|
options.AddProfile<AbpLoggingSerilogElasticsearchMapperProfile>(validate: true); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
public class AbpLoggingSerilogElasticsearchOptions |
||||||
|
{ |
||||||
|
public string IndexFormat { get; set; } |
||||||
|
|
||||||
|
public AbpLoggingSerilogElasticsearchOptions() |
||||||
|
{ |
||||||
|
IndexFormat = "logstash-{0:yyyy.MM.dd}"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,412 @@ |
|||||||
|
using Sanhe.Abp.Elasticsearch; |
||||||
|
using Sanhe.Abp.Serilog.Enrichers.Application; |
||||||
|
using Sanhe.Abp.Serilog.Enrichers.UniqueId; |
||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||||
|
using Microsoft.Extensions.Options; |
||||||
|
using Nest; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Text.RegularExpressions; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Volo.Abp.DependencyInjection; |
||||||
|
using Volo.Abp.MultiTenancy; |
||||||
|
using Volo.Abp.ObjectMapping; |
||||||
|
using Sanhe.Abp.Logging; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
[Dependency(ReplaceServices = true)] |
||||||
|
public class SerilogElasticsearchLoggingManager : ILoggingManager, ISingletonDependency |
||||||
|
{ |
||||||
|
private static readonly Regex IndexFormatRegex = new Regex(@"^(.*)(?:\{0\:.+\})(.*)$"); |
||||||
|
|
||||||
|
private readonly IObjectMapper _objectMapper; |
||||||
|
private readonly ICurrentTenant _currentTenant; |
||||||
|
private readonly AbpLoggingSerilogElasticsearchOptions _options; |
||||||
|
private readonly IElasticsearchClientFactory _clientFactory; |
||||||
|
|
||||||
|
public ILogger<SerilogElasticsearchLoggingManager> Logger { protected get; set; } |
||||||
|
|
||||||
|
public SerilogElasticsearchLoggingManager( |
||||||
|
IObjectMapper objectMapper, |
||||||
|
ICurrentTenant currentTenant, |
||||||
|
IOptions<AbpLoggingSerilogElasticsearchOptions> options, |
||||||
|
IElasticsearchClientFactory clientFactory) |
||||||
|
{ |
||||||
|
_objectMapper = objectMapper; |
||||||
|
_currentTenant = currentTenant; |
||||||
|
_clientFactory = clientFactory; |
||||||
|
_options = options.Value; |
||||||
|
|
||||||
|
Logger = NullLogger<SerilogElasticsearchLoggingManager>.Instance; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// |
||||||
|
/// </summary> |
||||||
|
/// <param name="id">时间类型或者转换为timestamp都可以查询</param> |
||||||
|
/// <param name="cancellationToken"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public virtual async Task<LogInfo> GetAsync( |
||||||
|
string id, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
ISearchResponse<SerilogInfo> response; |
||||||
|
|
||||||
|
if (_currentTenant.IsAvailable) |
||||||
|
{ |
||||||
|
/* |
||||||
|
"query": { |
||||||
|
"bool": { |
||||||
|
"must": [ |
||||||
|
{ |
||||||
|
"term": { |
||||||
|
"fields.TenantId.keyword": { |
||||||
|
"value": _currentTenant.GetId() |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"term": { |
||||||
|
"fields.UniqueId": { |
||||||
|
"value": "1474021081433481216" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
response = await client.SearchAsync<SerilogInfo>( |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query( |
||||||
|
(q) => q.Bool( |
||||||
|
(b) => b.Must( |
||||||
|
(s) => s.Term( |
||||||
|
(t) => t.Field(GetField(nameof(SerilogInfo.Fields.UniqueId))).Value(id)), |
||||||
|
(s) => s.Term( |
||||||
|
(t) => t.Field(GetField(nameof(SerilogInfo.Fields.TenantId))).Value(_currentTenant.GetId()))))), |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* |
||||||
|
"query": { |
||||||
|
"bool": { |
||||||
|
"must": [ |
||||||
|
{ |
||||||
|
"term": { |
||||||
|
"fields.UniqueId": { |
||||||
|
"value": "1474021081433481216" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
response = await client.SearchAsync<SerilogInfo>( |
||||||
|
dsl => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query( |
||||||
|
(q) => q.Bool( |
||||||
|
(b) => b.Must( |
||||||
|
(s) => s.Term( |
||||||
|
(t) => t.Field(GetField(nameof(SerilogInfo.Fields.UniqueId))).Value(id))))), |
||||||
|
cancellationToken); |
||||||
|
} |
||||||
|
|
||||||
|
return _objectMapper.Map<SerilogInfo, LogInfo>(response.Documents.FirstOrDefault()); |
||||||
|
} |
||||||
|
|
||||||
|
public virtual async Task<long> GetCountAsync( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
Microsoft.Extensions.Logging.LogLevel? level = null, |
||||||
|
string machineName = null, |
||||||
|
string environment = null, |
||||||
|
string application = null, |
||||||
|
string context = null, |
||||||
|
string requestId = null, |
||||||
|
string requestPath = null, |
||||||
|
string correlationId = null, |
||||||
|
int? processId = null, |
||||||
|
int? threadId = null, |
||||||
|
bool? hasException = null, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
level, |
||||||
|
machineName, |
||||||
|
environment, |
||||||
|
application, |
||||||
|
context, |
||||||
|
requestId, |
||||||
|
requestPath, |
||||||
|
correlationId, |
||||||
|
processId, |
||||||
|
threadId, |
||||||
|
hasException); |
||||||
|
|
||||||
|
var response = await client.CountAsync<SerilogInfo>((dsl) => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => log.Bool(b => b.Must(querys.ToArray()))), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return response.Count; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 获取日志列表 |
||||||
|
/// </summary> |
||||||
|
/// <param name="sorting">排序字段</param> |
||||||
|
/// <param name="maxResultCount"></param> |
||||||
|
/// <param name="skipCount"></param> |
||||||
|
/// <param name="startTime"></param> |
||||||
|
/// <param name="endTime"></param> |
||||||
|
/// <param name="level"></param> |
||||||
|
/// <param name="machineName"></param> |
||||||
|
/// <param name="environment"></param> |
||||||
|
/// <param name="application"></param> |
||||||
|
/// <param name="context"></param> |
||||||
|
/// <param name="requestId"></param> |
||||||
|
/// <param name="requestPath"></param> |
||||||
|
/// <param name="correlationId"></param> |
||||||
|
/// <param name="processId"></param> |
||||||
|
/// <param name="threadId"></param> |
||||||
|
/// <param name="hasException"></param> |
||||||
|
/// <param name="includeDetails"></param> |
||||||
|
/// <param name="cancellationToken"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public virtual async Task<List<LogInfo>> GetListAsync( |
||||||
|
string sorting = null, |
||||||
|
int maxResultCount = 50, |
||||||
|
int skipCount = 0, |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
Microsoft.Extensions.Logging.LogLevel? level = null, |
||||||
|
string machineName = null, |
||||||
|
string environment = null, |
||||||
|
string application = null, |
||||||
|
string context = null, |
||||||
|
string requestId = null, |
||||||
|
string requestPath = null, |
||||||
|
string correlationId = null, |
||||||
|
int? processId = null, |
||||||
|
int? threadId = null, |
||||||
|
bool? hasException = null, |
||||||
|
bool includeDetails = false, |
||||||
|
CancellationToken cancellationToken = default) |
||||||
|
{ |
||||||
|
var client = _clientFactory.Create(); |
||||||
|
|
||||||
|
var sortOrder = !sorting.IsNullOrWhiteSpace() && sorting.EndsWith("asc", StringComparison.InvariantCultureIgnoreCase) |
||||||
|
? SortOrder.Ascending : SortOrder.Descending; |
||||||
|
sorting = !sorting.IsNullOrWhiteSpace() |
||||||
|
? sorting.Split()[0] |
||||||
|
: nameof(SerilogInfo.TimeStamp); |
||||||
|
|
||||||
|
var querys = BuildQueryDescriptor( |
||||||
|
startTime, |
||||||
|
endTime, |
||||||
|
level, |
||||||
|
machineName, |
||||||
|
environment, |
||||||
|
application, |
||||||
|
context, |
||||||
|
requestId, |
||||||
|
requestPath, |
||||||
|
correlationId, |
||||||
|
processId, |
||||||
|
threadId, |
||||||
|
hasException); |
||||||
|
|
||||||
|
SourceFilterDescriptor<SerilogInfo> SourceFilter(SourceFilterDescriptor<SerilogInfo> selector) |
||||||
|
{ |
||||||
|
selector.IncludeAll(); |
||||||
|
if (!includeDetails) |
||||||
|
{ |
||||||
|
selector.Excludes(field => |
||||||
|
field.Field("exceptions")); |
||||||
|
} |
||||||
|
|
||||||
|
return selector; |
||||||
|
} |
||||||
|
|
||||||
|
var response = await client.SearchAsync<SerilogInfo>((dsl) => |
||||||
|
dsl.Index(CreateIndex()) |
||||||
|
.Query(log => |
||||||
|
log.Bool(b => |
||||||
|
b.Must(querys.ToArray()))) |
||||||
|
.Source(SourceFilter) |
||||||
|
.Sort(log => log.Field(GetField(sorting), sortOrder)) |
||||||
|
.From(skipCount) |
||||||
|
.Size(maxResultCount), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
|
return _objectMapper.Map<List<SerilogInfo>, List<LogInfo>>(response.Documents.ToList()); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual List<Func<QueryContainerDescriptor<SerilogInfo>, QueryContainer>> BuildQueryDescriptor( |
||||||
|
DateTime? startTime = null, |
||||||
|
DateTime? endTime = null, |
||||||
|
Microsoft.Extensions.Logging.LogLevel? level = null, |
||||||
|
string machineName = null, |
||||||
|
string environment = null, |
||||||
|
string application = null, |
||||||
|
string context = null, |
||||||
|
string requestId = null, |
||||||
|
string requestPath = null, |
||||||
|
string correlationId = null, |
||||||
|
int? processId = null, |
||||||
|
int? threadId = null, |
||||||
|
bool? hasException = null) |
||||||
|
{ |
||||||
|
var querys = new List<Func<QueryContainerDescriptor<SerilogInfo>, QueryContainer>>(); |
||||||
|
|
||||||
|
if (_currentTenant.IsAvailable) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.TenantId))).Value(_currentTenant.GetId()))); |
||||||
|
} |
||||||
|
if (startTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SerilogInfo.TimeStamp))).GreaterThanOrEquals(startTime))); |
||||||
|
} |
||||||
|
if (endTime.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.DateRange((q) => q.Field(GetField(nameof(SerilogInfo.TimeStamp))).LessThanOrEquals(endTime))); |
||||||
|
} |
||||||
|
if (level.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Level))).Value(level.ToString()))); |
||||||
|
} |
||||||
|
if (!machineName.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.MachineName))).Value(machineName))); |
||||||
|
} |
||||||
|
if (!environment.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Environment))).Value(environment))); |
||||||
|
} |
||||||
|
if (!application.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Application))).Value(application))); |
||||||
|
} |
||||||
|
if (!context.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.Context))).Value(context))); |
||||||
|
} |
||||||
|
if (!requestId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.RequestId))).Value(requestId))); |
||||||
|
} |
||||||
|
if (!requestPath.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
// 模糊匹配 |
||||||
|
querys.Add((log) => log.MatchPhrasePrefix((q) => q.Field(f => f.Fields.RequestPath).Query(requestPath))); |
||||||
|
} |
||||||
|
if (!correlationId.IsNullOrWhiteSpace()) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.MatchPhrase((q) => q.Field(GetField(nameof(SerilogInfo.Fields.CorrelationId))).Query(correlationId))); |
||||||
|
} |
||||||
|
if (processId.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.ProcessId))).Value(processId))); |
||||||
|
} |
||||||
|
if (threadId.HasValue) |
||||||
|
{ |
||||||
|
querys.Add((log) => log.Term((q) => q.Field(GetField(nameof(SerilogInfo.Fields.ThreadId))).Value(threadId))); |
||||||
|
} |
||||||
|
|
||||||
|
if (hasException.HasValue) |
||||||
|
{ |
||||||
|
if (hasException.Value) |
||||||
|
{ |
||||||
|
/* 存在exceptions字段则就是有异常信息 |
||||||
|
* "exists": { |
||||||
|
"field": "exceptions" |
||||||
|
} |
||||||
|
*/ |
||||||
|
querys.Add( |
||||||
|
(q) => q.Exists( |
||||||
|
(e) => e.Field("exceptions"))); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// 不存在 exceptions字段就是没有异常信息的消息 |
||||||
|
/* |
||||||
|
* "bool": { |
||||||
|
"must_not": [ |
||||||
|
{ |
||||||
|
"exists": { |
||||||
|
"field": "exceptions" |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
*/ |
||||||
|
querys.Add( |
||||||
|
(q) => q.Bool( |
||||||
|
(b) => b.MustNot( |
||||||
|
(m) => m.Exists( |
||||||
|
(e) => e.Field("exceptions"))))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return querys; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual string CreateIndex(DateTimeOffset? offset = null) |
||||||
|
{ |
||||||
|
if (!offset.HasValue) |
||||||
|
{ |
||||||
|
return IndexFormatRegex.Replace(_options.IndexFormat, @"$1*$2"); |
||||||
|
} |
||||||
|
return string.Format(_options.IndexFormat, offset.Value).ToLowerInvariant(); |
||||||
|
} |
||||||
|
|
||||||
|
private readonly static IDictionary<string, string> _fieldMaps = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) |
||||||
|
{ |
||||||
|
{ "timestamp", "@timestamp" }, |
||||||
|
{ "level", "level.keyword" }, |
||||||
|
{ "machinename", $"fields.{AbpLoggingEnricherPropertyNames.MachineName}.keyword" }, |
||||||
|
{ "environment", $"fields.{AbpLoggingEnricherPropertyNames.EnvironmentName}.keyword" }, |
||||||
|
{ "application", $"fields.{AbpSerilogEnrichersConsts.ApplicationNamePropertyName}.keyword" }, |
||||||
|
{ "context", "fields.Context.keyword" }, |
||||||
|
{ "actionid", "fields.ActionId.keyword" }, |
||||||
|
{ "actionname", "fields.ActionName.keyword" }, |
||||||
|
{ "requestid", "fields.RequestId.keyword" }, |
||||||
|
{ "requestpath", "fields.RequestPath.keyword" }, |
||||||
|
{ "connectionid", "fields.ConnectionId.keyword" }, |
||||||
|
{ "correlationid", "fields.CorrelationId.keyword" }, |
||||||
|
{ "clientid", "fields.ClientId.keyword" }, |
||||||
|
{ "userid", "fields.UserId.keyword" }, |
||||||
|
{ "processid", "fields.ProcessId" }, |
||||||
|
{ "threadid", "fields.ThreadId" }, |
||||||
|
{ "id", $"fields.{AbpSerilogUniqueIdConsts.UniqueIdPropertyName}" }, |
||||||
|
{ "uniqueid", $"fields.{AbpSerilogUniqueIdConsts.UniqueIdPropertyName}" }, |
||||||
|
}; |
||||||
|
protected virtual string GetField(string field) |
||||||
|
{ |
||||||
|
foreach (var fieldMap in _fieldMaps) |
||||||
|
{ |
||||||
|
if (field.ToLowerInvariant().Contains(fieldMap.Key)) |
||||||
|
{ |
||||||
|
return fieldMap.Value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return field; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
public class SerilogException |
||||||
|
{ |
||||||
|
[Nest.PropertyName("SourceContext")] |
||||||
|
public int Depth { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ClassName")] |
||||||
|
public string Class { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("Message")] |
||||||
|
public string Message { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("Source")] |
||||||
|
public string Source { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("StackTraceString")] |
||||||
|
public string StackTrace { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("HResult")] |
||||||
|
public int HResult { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("HelpURL")] |
||||||
|
public string HelpURL { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
using Sanhe.Abp.Logging; |
||||||
|
using Sanhe.Abp.Serilog.Enrichers.Application; |
||||||
|
using Sanhe.Abp.Serilog.Enrichers.UniqueId; |
||||||
|
using System; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
public class SerilogField |
||||||
|
{ |
||||||
|
[Nest.PropertyName(AbpSerilogUniqueIdConsts.UniqueIdPropertyName)] |
||||||
|
public long UniqueId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName(AbpLoggingEnricherPropertyNames.MachineName)] |
||||||
|
public string MachineName { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName(AbpLoggingEnricherPropertyNames.EnvironmentName)] |
||||||
|
public string Environment { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName(AbpSerilogEnrichersConsts.ApplicationNamePropertyName)] |
||||||
|
public string Application { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("SourceContext")] |
||||||
|
public string Context { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ActionId")] |
||||||
|
public string ActionId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ActionName")] |
||||||
|
public string ActionName { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("RequestId")] |
||||||
|
public string RequestId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("RequestPath")] |
||||||
|
public string RequestPath { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ConnectionId")] |
||||||
|
public string ConnectionId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("CorrelationId")] |
||||||
|
public string CorrelationId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ClientId")] |
||||||
|
public string ClientId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("UserId")] |
||||||
|
public string UserId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("TenantId")] |
||||||
|
public Guid? TenantId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ProcessId")] |
||||||
|
public int ProcessId { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("ThreadId")] |
||||||
|
public int ThreadId { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using Serilog.Formatting.Elasticsearch; |
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.AuditLogging.Serilog.Elasticsearch |
||||||
|
{ |
||||||
|
[Serializable] |
||||||
|
public class SerilogInfo |
||||||
|
{ |
||||||
|
[Nest.PropertyName(ElasticsearchJsonFormatter.TimestampPropertyName)] |
||||||
|
public DateTime TimeStamp { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName(ElasticsearchJsonFormatter.LevelPropertyName)] |
||||||
|
public LogLevel Level { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName(ElasticsearchJsonFormatter.RenderedMessagePropertyName)] |
||||||
|
public string Message { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("fields")] |
||||||
|
public SerilogField Fields { get; set; } |
||||||
|
|
||||||
|
[Nest.PropertyName("exceptions")] |
||||||
|
public List<SerilogException> Exceptions { get; set; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||||
|
</Weavers> |
@ -0,0 +1,15 @@ |
|||||||
|
# LINGYUN.Abp.Logging |
||||||
|
|
||||||
|
日志基础模块 |
||||||
|
|
||||||
|
定义 ILoggingManager 接口, 实现日志信息查询 |
||||||
|
|
||||||
|
## 模块引用 |
||||||
|
|
||||||
|
```csharp |
||||||
|
[DependsOn(typeof(AbpLoggingModule))] |
||||||
|
public class YouProjectModule : AbpModule |
||||||
|
{ |
||||||
|
// other |
||||||
|
} |
||||||
|
``` |
@ -0,0 +1,14 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||||
|
<Import Project="..\..\..\common.props" /> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||||
|
<RootNamespace /> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="Volo.Abp.Core" Version="$(VoloAbpVersion)" /> |
||||||
|
</ItemGroup> |
||||||
|
</Project> |
@ -0,0 +1,8 @@ |
|||||||
|
namespace Sanhe.Abp.Logging |
||||||
|
{ |
||||||
|
public class AbpLoggingEnricherPropertyNames |
||||||
|
{ |
||||||
|
public const string MachineName = "MachineName"; |
||||||
|
public const string EnvironmentName = "EnvironmentName"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
using Microsoft.Extensions.DependencyInjection; |
||||||
|
using Volo.Abp.Modularity; |
||||||
|
|
||||||
|
namespace Sanhe.Abp.Logging |
||||||
|
{ |
||||||
|
public class AbpLoggingModule : AbpModule |
||||||
|
{ |
||||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||||
|
{ |
||||||
|
var configuration = context.Services.GetConfiguration(); |
||||||
|
|
||||||
|
Configure<AbpLoggingEnricherPropertyNames>(configuration.GetSection("Logging")); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue