mirror of https://github.com/abpframework/abp.git
55 changed files with 701 additions and 224 deletions
@ -1,27 +0,0 @@ |
|||
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; |
|||
using Volo.Abp.AspNetCore.Mvc.Uow; |
|||
|
|||
namespace Microsoft.AspNetCore.Builder |
|||
{ |
|||
public static class AbpAspNetCoreMvcApplicationBuilderExtensions |
|||
{ |
|||
public static IApplicationBuilder UseUnitOfWork(this IApplicationBuilder app) |
|||
{ |
|||
return app |
|||
.UseAbpExceptionHandling() |
|||
.UseMiddleware<AbpUnitOfWorkMiddleware>(); |
|||
} |
|||
|
|||
public static IApplicationBuilder UseAbpExceptionHandling(this IApplicationBuilder app) |
|||
{ |
|||
//Prevent multiple add
|
|||
if (app.Properties.ContainsKey("_AbpExceptionHandlingMiddleware_Added")) //TODO: Constant
|
|||
{ |
|||
return app; |
|||
} |
|||
|
|||
app.Properties["_AbpExceptionHandlingMiddleware_Added"] = true; |
|||
return app.UseMiddleware<AbpExceptionHandlingMiddleware>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
namespace Volo.Abp.AspNetCore.Mvc |
|||
{ |
|||
public class AbpActionInfoInHttpContext |
|||
public class AbpActionInfoInHttpContext //Rename?
|
|||
{ |
|||
public bool IsObjectResult { get; set; } |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Auditing |
|||
{ |
|||
public class AbpAuditingMiddleware |
|||
{ |
|||
private readonly RequestDelegate _next; |
|||
private readonly IAuditingManager _auditingManager; |
|||
|
|||
public AbpAuditingMiddleware(RequestDelegate next, IAuditingManager auditingManager) |
|||
{ |
|||
_next = next; |
|||
_auditingManager = auditingManager; |
|||
} |
|||
|
|||
public async Task Invoke(HttpContext httpContext) |
|||
{ |
|||
using (var scope = _auditingManager.BeginScope()) |
|||
{ |
|||
try |
|||
{ |
|||
await _next(httpContext); |
|||
} |
|||
finally |
|||
{ |
|||
await scope.SaveAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +1,10 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Primitives; |
|||
using Microsoft.Net.Http.Headers; |
|||
using Volo.Abp.AspNetCore.Mvc.Uow; |
|||
using Volo.Abp.AspNetCore.Uow; |
|||
using Volo.Abp.Http; |
|||
using Volo.Abp.Json; |
|||
|
|||
@ -1,55 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Data; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditInfo : IHasExtraProperties |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid? UserId { get; set; } |
|||
|
|||
public Guid? ImpersonatorUserId { get; set; } |
|||
|
|||
public Guid? ImpersonatorTenantId { 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 string ClientIpAddress { get; set; } |
|||
|
|||
public string ClientName { get; set; } |
|||
|
|||
public string BrowserInfo { get; set; } |
|||
|
|||
public Exception Exception { get; set; } |
|||
|
|||
public Dictionary<string, object> ExtraProperties { get; } |
|||
|
|||
public AuditInfo() |
|||
{ |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
var loggedUserId = UserId.HasValue |
|||
? "user " + UserId.Value |
|||
: "an anonymous user"; |
|||
|
|||
var exceptionOrSuccessMessage = Exception != null |
|||
? "exception: " + Exception.Message |
|||
: "succeed"; |
|||
|
|||
return $"AUDIT LOG: {ServiceName}.{MethodName} is executed by {loggedUserId} in {ExecutionDuration} ms from {ClientIpAddress} IP address with {exceptionOrSuccessMessage}."; |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditInfoContributionContext : IAuditInfoContributionContext |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
public AuditInfo AuditInfo { get; set; } |
|||
|
|||
public AuditInfoContributionContext(IServiceProvider serviceProvider, AuditInfo auditInfo) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
AuditInfo = auditInfo; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditLogActionInfo |
|||
{ |
|||
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 Dictionary<string, object> ExtraProperties { get; } |
|||
|
|||
public AuditLogActionInfo() |
|||
{ |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditLogContributionContext : IServiceProviderAccessor |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
public AuditLogInfo AuditInfo { get; } |
|||
|
|||
public AuditLogContributionContext(IServiceProvider serviceProvider, AuditLogInfo auditInfo) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
AuditInfo = auditInfo; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public abstract class AuditLogContributor |
|||
{ |
|||
public abstract Task ContributeAsync(AuditLogContributionContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditLogInfo : IMultiTenant |
|||
{ |
|||
public Guid? UserId { get; set; } |
|||
|
|||
public Guid? TenantId { 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 BrowserInfo { get; set; } |
|||
|
|||
public List<AuditLogActionInfo> Actions { get; set; } |
|||
|
|||
public List<Exception> Exceptions { get; } |
|||
|
|||
public Dictionary<string, object> ExtraProperties { get; } |
|||
|
|||
public IList<EntityChangeInfo> EntityChanges { get; } |
|||
|
|||
public AuditLogInfo() |
|||
{ |
|||
Actions = new List<AuditLogActionInfo>(); |
|||
Exceptions = new List<Exception>(); |
|||
ExtraProperties = new Dictionary<string, object>(); |
|||
EntityChanges = new List<EntityChangeInfo>(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
var sb = new StringBuilder(); |
|||
|
|||
sb.AppendLine("AUDIT LOG:"); |
|||
sb.AppendLine($"- UserId : {UserId}"); |
|||
sb.AppendLine($"- ClientIpAddress : {ClientIpAddress}"); |
|||
sb.AppendLine($"- ExecutionDuration : {ExecutionDuration}"); |
|||
|
|||
if (Actions.Any()) |
|||
{ |
|||
sb.AppendLine("- Actions:"); |
|||
foreach (var action in Actions) |
|||
{ |
|||
sb.AppendLine($" - {action.ServiceName}.{action.MethodName} ({action.ExecutionDuration} ms.)"); |
|||
sb.AppendLine($" - {action.Parameters}"); |
|||
} |
|||
} |
|||
|
|||
if (Exceptions.Any()) |
|||
{ |
|||
sb.AppendLine("- Exceptions:"); |
|||
foreach (var exception in Exceptions) |
|||
{ |
|||
sb.AppendLine($" - {exception.Message}"); |
|||
sb.AppendLine($" - {exception}"); |
|||
} |
|||
} |
|||
|
|||
//TODO: EntityChanges
|
|||
|
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditLogScope : IAuditLogScope |
|||
{ |
|||
public AuditLogInfo Log { get; } |
|||
|
|||
public AuditLogScope(AuditLogInfo log) |
|||
{ |
|||
Log = log; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditLogScope |
|||
{ |
|||
[NotNull] |
|||
AuditLogInfo Log { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingManager : IAuditingManager, ITransientDependency |
|||
{ |
|||
private const string AmbientContextKey = "Volo.Abp.Auditing.IAuditLogScope"; |
|||
|
|||
private readonly IAmbientScopeProvider<IAuditLogScope> _ambientScopeProvider; |
|||
private readonly IAuditingHelper _auditingHelper; |
|||
private readonly IAuditingStore _auditingStore; |
|||
|
|||
public AuditingManager( |
|||
IAmbientScopeProvider<IAuditLogScope> ambientScopeProvider, |
|||
IAuditingHelper auditingHelper, |
|||
IAuditingStore auditingStore) |
|||
{ |
|||
_ambientScopeProvider = ambientScopeProvider; |
|||
_auditingHelper = auditingHelper; |
|||
_auditingStore = auditingStore; |
|||
} |
|||
|
|||
public IAuditLogScope Current => _ambientScopeProvider.GetValue(AmbientContextKey); |
|||
|
|||
public IAuditLogSaveHandle BeginScope() |
|||
{ |
|||
var ambientScope = _ambientScopeProvider.BeginScope( |
|||
AmbientContextKey, |
|||
new AuditLogScope(_auditingHelper.CreateAuditLogInfo()) |
|||
); |
|||
|
|||
Debug.Assert(Current != null, "Current != null"); |
|||
|
|||
var stopWatch = Stopwatch.StartNew(); |
|||
|
|||
return new DisposableSaveHandle(_auditingStore, ambientScope, Current.Log, stopWatch); |
|||
} |
|||
|
|||
private class DisposableSaveHandle : IAuditLogSaveHandle |
|||
{ |
|||
private readonly IAuditingStore _auditingStore; |
|||
private readonly IDisposable _scope; |
|||
private readonly AuditLogInfo _auditLog; |
|||
private readonly Stopwatch _stopWatch; |
|||
|
|||
private bool _saved; |
|||
|
|||
public DisposableSaveHandle(IAuditingStore auditingStore, IDisposable scope, AuditLogInfo auditLog, Stopwatch stopWatch) |
|||
{ |
|||
_auditingStore = auditingStore; |
|||
_scope = scope; |
|||
_auditLog = auditLog; |
|||
_stopWatch = stopWatch; |
|||
} |
|||
|
|||
private void BeforeSave() |
|||
{ |
|||
_stopWatch.Stop(); |
|||
_saved = true; |
|||
_auditLog.ExecutionDuration = Convert.ToInt32(_stopWatch.Elapsed.TotalMilliseconds); |
|||
} |
|||
|
|||
public async Task SaveAsync() |
|||
{ |
|||
BeforeSave(); |
|||
await _auditingStore.SaveAsync(_auditLog); |
|||
} |
|||
|
|||
public void Save() |
|||
{ |
|||
BeforeSave(); |
|||
_auditingStore.Save(_auditLog); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if (!_saved) |
|||
{ |
|||
Save(); |
|||
} |
|||
|
|||
_scope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel.DataAnnotations.Schema; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class EntityChangeInfo : IMultiTenant |
|||
{ |
|||
public DateTime ChangeTime { get; set; } |
|||
|
|||
public EntityChangeType ChangeType { get; set; } |
|||
|
|||
public string EntityId { get; set; } |
|||
|
|||
public string EntityTypeFullName { get; set; } |
|||
|
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public ICollection<EntityPropertyChangeInfo> PropertyChanges { get; set; } |
|||
|
|||
#region Not mapped
|
|||
|
|||
[NotMapped] |
|||
public virtual object EntityEntry { get; set; } //TODO: ???
|
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace Volo.Abp.Domain.Entities.Events |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public enum EntityChangeType : byte |
|||
{ |
|||
@ -0,0 +1,13 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class EntityPropertyChangeInfo |
|||
{ |
|||
public virtual string NewValue { get; set; } |
|||
|
|||
public virtual string OriginalValue { get; set; } |
|||
|
|||
public virtual string PropertyName { get; set; } |
|||
|
|||
public virtual string PropertyTypeFullName { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditInfoContributionContext : IServiceProviderAccessor |
|||
{ |
|||
AuditInfo AuditInfo { get; set; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditInfoContributor |
|||
{ |
|||
Task Contribute(IAuditInfoContributionContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditLogSaveHandle : IDisposable |
|||
{ |
|||
void Save(); |
|||
|
|||
Task SaveAsync(); |
|||
} |
|||
} |
|||
@ -1,20 +1,18 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
//TODO: Move ShouldSaveAudit and rename to IAuditingFactory
|
|||
public interface IAuditingHelper |
|||
{ |
|||
bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false); |
|||
|
|||
AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments); |
|||
AuditLogInfo CreateAuditLogInfo(); |
|||
|
|||
AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments); |
|||
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments); |
|||
|
|||
void Save(AuditInfo auditInfo); |
|||
|
|||
Task SaveAsync(AuditInfo auditInfo); |
|||
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary<string, object> arguments); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditingManager |
|||
{ |
|||
[CanBeNull] |
|||
IAuditLogScope Current { get; } |
|||
|
|||
IAuditLogSaveHandle BeginScope(); |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
|
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
public class AmbientDataContextAmbientScopeProvider<T> : IAmbientScopeProvider<T> |
|||
{ |
|||
public ILogger<AmbientDataContextAmbientScopeProvider<T>> Logger { get; set; } |
|||
|
|||
private static readonly ConcurrentDictionary<string, ScopeItem> ScopeDictionary = new ConcurrentDictionary<string, ScopeItem>(); |
|||
|
|||
private readonly IAmbientDataContext _dataContext; |
|||
|
|||
public AmbientDataContextAmbientScopeProvider([NotNull] IAmbientDataContext dataContext) |
|||
{ |
|||
Check.NotNull(dataContext, nameof(dataContext)); |
|||
|
|||
_dataContext = dataContext; |
|||
|
|||
Logger = NullLogger<AmbientDataContextAmbientScopeProvider<T>>.Instance; |
|||
} |
|||
|
|||
public T GetValue(string contextKey) |
|||
{ |
|||
var item = GetCurrentItem(contextKey); |
|||
if (item == null) |
|||
{ |
|||
return default; |
|||
} |
|||
|
|||
return item.Value; |
|||
} |
|||
|
|||
public IDisposable BeginScope(string contextKey, T value) |
|||
{ |
|||
var item = new ScopeItem(value, GetCurrentItem(contextKey)); |
|||
|
|||
if (!ScopeDictionary.TryAdd(item.Id, item)) |
|||
{ |
|||
throw new AbpException("Can not add item! ScopeDictionary.TryAdd returns false!"); |
|||
} |
|||
|
|||
_dataContext.SetData(contextKey, item.Id); |
|||
|
|||
return new DisposeAction(() => |
|||
{ |
|||
ScopeDictionary.TryRemove(item.Id, out item); |
|||
|
|||
if (item.Outer == null) |
|||
{ |
|||
_dataContext.SetData(contextKey, null); |
|||
return; |
|||
} |
|||
|
|||
_dataContext.SetData(contextKey, item.Outer.Id); |
|||
}); |
|||
} |
|||
|
|||
private ScopeItem GetCurrentItem(string contextKey) |
|||
{ |
|||
var objKey = _dataContext.GetData(contextKey) as string; |
|||
return objKey != null ? ScopeDictionary.GetOrDefault(objKey) : null; |
|||
} |
|||
|
|||
private class ScopeItem |
|||
{ |
|||
public string Id { get; } |
|||
|
|||
public ScopeItem Outer { get; } |
|||
|
|||
public T Value { get; } |
|||
|
|||
public ScopeItem(T value, ScopeItem outer = null) |
|||
{ |
|||
Id = Guid.NewGuid().ToString(); |
|||
|
|||
Value = value; |
|||
Outer = outer; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System.Collections.Concurrent; |
|||
using System.Threading; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
public class AsyncLocalAmbientDataContext : IAmbientDataContext, ISingletonDependency |
|||
{ |
|||
private static readonly ConcurrentDictionary<string, AsyncLocal<object>> AsyncLocalDictionary = new ConcurrentDictionary<string, AsyncLocal<object>>(); |
|||
|
|||
public void SetData(string key, object value) |
|||
{ |
|||
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>()); |
|||
asyncLocal.Value = value; |
|||
} |
|||
|
|||
public object GetData(string key) |
|||
{ |
|||
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>()); |
|||
return asyncLocal.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
public interface IAmbientDataContext |
|||
{ |
|||
void SetData(string key, object value); |
|||
|
|||
object GetData(string key); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
public interface IAmbientScopeProvider<T> |
|||
{ |
|||
T GetValue(string contextKey); |
|||
|
|||
IDisposable BeginScope(string contextKey, T value); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue