Browse Source

Resolved #3328: Allow to subscribe to exceptions handled by the abp framework

pull/3334/head
Halil İbrahim Kalkan 7 years ago
parent
commit
e849921f7e
  1. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Controllers/ErrorController.cs
  2. 18
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  3. 8
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  4. 6
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs
  5. 6
      framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs
  6. 19
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs
  7. 18
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs
  8. 44
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  9. 32
      framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotificationContext.cs
  10. 41
      framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotifier.cs
  11. 27
      framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotifierExtensions.cs
  12. 10
      framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/IExceptionNotifier.cs
  13. 10
      framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/IExceptionSubscriber.cs
  14. 15
      framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapManager.cs
  15. 11
      framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs
  16. 20
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs
  17. 23
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Controllers/ErrorController.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Localization.Resources.AbpUi; using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -7,6 +8,7 @@ using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Views.Error; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Views.Error;
using Volo.Abp.ExceptionHandling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Controllers namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Controllers
{ {
@ -16,20 +18,23 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Controllers
private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder; private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder;
private readonly IStringLocalizer<AbpUiResource> _localizer; private readonly IStringLocalizer<AbpUiResource> _localizer;
private readonly AbpErrorPageOptions _abpErrorPageOptions; private readonly AbpErrorPageOptions _abpErrorPageOptions;
private readonly IExceptionNotifier _exceptionNotifier;
public ErrorController( public ErrorController(
IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IExceptionToErrorInfoConverter exceptionToErrorInfoConverter,
IHttpExceptionStatusCodeFinder httpExceptionStatusCodeFinder, IHttpExceptionStatusCodeFinder httpExceptionStatusCodeFinder,
IOptions<AbpErrorPageOptions> abpErrorPageOptions, IOptions<AbpErrorPageOptions> abpErrorPageOptions,
IStringLocalizer<AbpUiResource> localizer) IStringLocalizer<AbpUiResource> localizer,
IExceptionNotifier exceptionNotifier)
{ {
_errorInfoConverter = exceptionToErrorInfoConverter; _errorInfoConverter = exceptionToErrorInfoConverter;
_statusCodeFinder = httpExceptionStatusCodeFinder; _statusCodeFinder = httpExceptionStatusCodeFinder;
_localizer = localizer; _localizer = localizer;
_exceptionNotifier = exceptionNotifier;
_abpErrorPageOptions = abpErrorPageOptions.Value; _abpErrorPageOptions = abpErrorPageOptions.Value;
} }
public IActionResult Index(int httpStatusCode) public async Task<IActionResult> Index(int httpStatusCode)
{ {
var exHandlerFeature = HttpContext.Features.Get<IExceptionHandlerFeature>(); var exHandlerFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();
@ -37,6 +42,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Controllers
? exHandlerFeature.Error ? exHandlerFeature.Error
: new Exception(_localizer["UnhandledException"]); : new Exception(_localizer["UnhandledException"]);
await _exceptionNotifier.NotifyAsync(new ExceptionNotificationContext(exception));
var errorInfo = _errorInfoConverter.Convert(exception); var errorInfo = _errorInfoConverter.Convert(exception);
if (httpStatusCode == 0) if (httpStatusCode == 0)

18
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -1,18 +1,21 @@
using System; using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http; using Volo.Abp.Http;
using Volo.Abp.Json; using Volo.Abp.Json;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{ {
public class AbpExceptionFilter : IExceptionFilter, ITransientDependency public class AbpExceptionFilter : IAsyncExceptionFilter, ITransientDependency
{ {
public ILogger<AbpExceptionFilter> Logger { get; set; } public ILogger<AbpExceptionFilter> Logger { get; set; }
@ -32,14 +35,14 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
Logger = NullLogger<AbpExceptionFilter>.Instance; Logger = NullLogger<AbpExceptionFilter>.Instance;
} }
public virtual void OnException(ExceptionContext context) public async Task OnExceptionAsync(ExceptionContext context)
{ {
if (!ShouldHandleException(context)) if (!ShouldHandleException(context))
{ {
return; return;
} }
HandleAndWrapException(context); await HandleAndWrapException(context);
} }
protected virtual bool ShouldHandleException(ExceptionContext context) protected virtual bool ShouldHandleException(ExceptionContext context)
@ -65,7 +68,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
return false; return false;
} }
protected virtual void HandleAndWrapException(ExceptionContext context) protected virtual async Task HandleAndWrapException(ExceptionContext context)
{ {
//TODO: Trigger an AbpExceptionHandled event or something like that. //TODO: Trigger an AbpExceptionHandled event or something like that.
@ -82,6 +85,13 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true)); Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
Logger.LogException(context.Exception, logLevel); Logger.LogException(context.Exception, logLevel);
await context.HttpContext
.RequestServices
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(
new ExceptionNotificationContext(context.Exception)
);
context.Exception = null; //Handled! context.Exception = null; //Handled!
} }
} }

8
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -7,6 +7,7 @@ using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Uow; using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http; using Volo.Abp.Http;
using Volo.Abp.Json; using Volo.Abp.Json;
@ -73,6 +74,13 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
) )
) )
); );
await httpContext
.RequestServices
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(
new ExceptionNotificationContext(exception)
);
} }
private Task ClearCacheHeaders(object state) private Task ClearCacheHeaders(object state)

6
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/BackgroundJobExecuter.cs

@ -3,7 +3,9 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
namespace Volo.Abp.BackgroundJobs namespace Volo.Abp.BackgroundJobs
{ {
@ -51,6 +53,10 @@ namespace Volo.Abp.BackgroundJobs
{ {
Logger.LogException(ex); Logger.LogException(ex);
await context.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(new ExceptionNotificationContext(ex));
throw new BackgroundJobExecutionException("A background job execution is failed. See inner exception for details.", ex) throw new BackgroundJobExecutionException("A background job execution is failed. See inner exception for details.", ex)
{ {
JobType = context.JobType.AssemblyQualifiedName, JobType = context.JobType.AssemblyQualifiedName,

6
framework/src/Volo.Abp.BackgroundJobs.RabbitMQ/Volo/Abp/BackgroundJobs/RabbitMQ/JobQueue.cs

@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
using Nito.AsyncEx; using Nito.AsyncEx;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.RabbitMQ; using Volo.Abp.RabbitMQ;
using Volo.Abp.Threading; using Volo.Abp.Threading;
@ -31,6 +32,7 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
protected IRabbitMqSerializer Serializer { get; } protected IRabbitMqSerializer Serializer { get; }
protected IBackgroundJobExecuter JobExecuter { get; } protected IBackgroundJobExecuter JobExecuter { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; } protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IExceptionNotifier ExceptionNotifier { get; }
protected SemaphoreSlim SyncObj = new SemaphoreSlim(1, 1); protected SemaphoreSlim SyncObj = new SemaphoreSlim(1, 1);
protected bool IsDiposed { get; private set; } protected bool IsDiposed { get; private set; }
@ -41,13 +43,15 @@ namespace Volo.Abp.BackgroundJobs.RabbitMQ
IChannelPool channelPool, IChannelPool channelPool,
IRabbitMqSerializer serializer, IRabbitMqSerializer serializer,
IBackgroundJobExecuter jobExecuter, IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory) IServiceScopeFactory serviceScopeFactory,
IExceptionNotifier exceptionNotifier)
{ {
AbpBackgroundJobOptions = backgroundJobOptions.Value; AbpBackgroundJobOptions = backgroundJobOptions.Value;
AbpRabbitMqBackgroundJobOptions = rabbitMqAbpBackgroundJobOptions.Value; AbpRabbitMqBackgroundJobOptions = rabbitMqAbpBackgroundJobOptions.Value;
Serializer = serializer; Serializer = serializer;
JobExecuter = jobExecuter; JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
ExceptionNotifier = exceptionNotifier;
ChannelPool = channelPool; ChannelPool = channelPool;
JobConfiguration = AbpBackgroundJobOptions.GetJob(typeof(TArgs)); JobConfiguration = AbpBackgroundJobOptions.GetJob(typeof(TArgs));

19
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AsyncPeriodicBackgroundWorkerBase.cs

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundWorkers namespace Volo.Abp.BackgroundWorkers
@ -35,18 +36,24 @@ namespace Volo.Abp.BackgroundWorkers
private void Timer_Elapsed(object sender, System.EventArgs e) private void Timer_Elapsed(object sender, System.EventArgs e)
{ {
try using (var scope = ServiceScopeFactory.CreateScope())
{ {
using (var scope = ServiceScopeFactory.CreateScope()) try
{ {
AsyncHelper.RunSync( AsyncHelper.RunSync(
() => DoWorkAsync(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)) () => DoWorkAsync(new PeriodicBackgroundWorkerContext(scope.ServiceProvider))
); );
} }
} catch (Exception ex)
catch (Exception ex) {
{ AsyncHelper.RunSync(
Logger.LogException(ex); () => scope.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(new ExceptionNotificationContext(ex))
);
Logger.LogException(ex);
}
} }
} }

18
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.BackgroundWorkers namespace Volo.Abp.BackgroundWorkers
@ -38,16 +39,21 @@ namespace Volo.Abp.BackgroundWorkers
private void Timer_Elapsed(object sender, System.EventArgs e) private void Timer_Elapsed(object sender, System.EventArgs e)
{ {
try using (var scope = ServiceScopeFactory.CreateScope())
{ {
using (var scope = ServiceScopeFactory.CreateScope()) try
{ {
DoWork(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)); DoWork(new PeriodicBackgroundWorkerContext(scope.ServiceProvider));
} }
} catch (Exception ex)
catch (Exception ex) {
{ scope.ServiceProvider
Logger.LogException(ex); .GetRequiredService<IExceptionNotifier>()
.NotifyAsync(new ExceptionNotificationContext(ex));
Logger.LogException(ex);
}
} }
} }

44
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -2,10 +2,13 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Nito.AsyncEx; using Nito.AsyncEx;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
@ -23,12 +26,14 @@ namespace Volo.Abp.Caching
IDistributedCache cache, IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer, IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer) : base( IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory) : base(
distributedCacheOption: distributedCacheOption, distributedCacheOption: distributedCacheOption,
cache: cache, cache: cache,
cancellationTokenProvider: cancellationTokenProvider, cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer, serializer: serializer,
keyNormalizer: keyNormalizer) keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory)
{ {
} }
@ -56,6 +61,8 @@ namespace Volo.Abp.Caching
protected IDistributedCacheKeyNormalizer KeyNormalizer { get; } protected IDistributedCacheKeyNormalizer KeyNormalizer { get; }
protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected SemaphoreSlim SyncSemaphore { get; } protected SemaphoreSlim SyncSemaphore { get; }
protected DistributedCacheEntryOptions DefaultCacheOptions; protected DistributedCacheEntryOptions DefaultCacheOptions;
@ -67,7 +74,8 @@ namespace Volo.Abp.Caching
IDistributedCache cache, IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer, IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer) IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory)
{ {
_distributedCacheOption = distributedCacheOption.Value; _distributedCacheOption = distributedCacheOption.Value;
Cache = cache; Cache = cache;
@ -75,6 +83,7 @@ namespace Volo.Abp.Caching
Logger = NullLogger<DistributedCache<TCacheItem, TCacheKey>>.Instance; Logger = NullLogger<DistributedCache<TCacheItem, TCacheKey>>.Instance;
Serializer = serializer; Serializer = serializer;
KeyNormalizer = keyNormalizer; KeyNormalizer = keyNormalizer;
ServiceScopeFactory = serviceScopeFactory;
SyncSemaphore = new SemaphoreSlim(1, 1); SyncSemaphore = new SemaphoreSlim(1, 1);
@ -139,7 +148,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
return null; return null;
} }
@ -181,7 +190,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); await HandleExceptionAsync(ex);
return null; return null;
} }
@ -298,7 +307,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
return; return;
} }
@ -337,7 +346,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); await HandleExceptionAsync(ex);
return; return;
} }
@ -364,7 +373,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
return; return;
} }
@ -393,7 +402,7 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); await HandleExceptionAsync(ex);
return; return;
} }
@ -420,7 +429,8 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
return;
} }
throw; throw;
@ -449,12 +459,24 @@ namespace Volo.Abp.Caching
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
Logger.LogException(ex, LogLevel.Warning); await HandleExceptionAsync(ex);
return; return;
} }
throw; throw;
} }
} }
protected virtual async Task HandleExceptionAsync(Exception ex)
{
Logger.LogException(ex, LogLevel.Warning);
using (var scope = ServiceScopeFactory.CreateScope())
{
await scope.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));
}
}
} }
} }

32
framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotificationContext.cs

@ -0,0 +1,32 @@
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.ExceptionHandling
{
public class ExceptionNotificationContext
{
/// <summary>
/// The exception object.
/// </summary>
[NotNull]
public Exception Exception { get; }
public LogLevel LogLevel { get; }
/// <summary>
/// True, if it is handled.
/// </summary>
public bool Handled { get; }
public ExceptionNotificationContext(
[NotNull] Exception exception,
LogLevel? logLevel = null,
bool handled = true)
{
Exception = Check.NotNull(exception, nameof(exception));
LogLevel = logLevel ?? exception.GetLogLevel();
Handled = handled;
}
}
}

41
framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotifier.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.ExceptionHandling
{
public class ExceptionNotifier : IExceptionNotifier, ITransientDependency
{
public ILogger<ExceptionNotifier> Logger { get; set; }
protected IEnumerable<IExceptionSubscriber> ExceptionSubscribers { get; }
public ExceptionNotifier(IEnumerable<IExceptionSubscriber> exceptionSubscribers)
{
ExceptionSubscribers = exceptionSubscribers;
Logger = NullLogger<ExceptionNotifier>.Instance;
}
public virtual async Task NotifyAsync([NotNull] ExceptionNotificationContext context)
{
Check.NotNull(context, nameof(context));
foreach (var exceptionSubscriber in ExceptionSubscribers)
{
try
{
await exceptionSubscriber.HandleAsync(context);
}
catch (Exception e)
{
Logger.LogWarning($"Exception subscriber of type {exceptionSubscriber.GetType().AssemblyQualifiedName} has thrown an exception!");
Logger.LogException(e, LogLevel.Warning);
}
}
}
}
}

27
framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/ExceptionNotifierExtensions.cs

@ -0,0 +1,27 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.ExceptionHandling
{
public static class ExceptionNotifierExtensions
{
public static Task NotifyAsync(
[NotNull] this IExceptionNotifier exceptionNotifier,
[NotNull] Exception exception,
LogLevel? logLevel = null,
bool handled = true)
{
Check.NotNull(exceptionNotifier, nameof(exceptionNotifier));
return exceptionNotifier.NotifyAsync(
new ExceptionNotificationContext(
exception,
logLevel,
handled
)
);
}
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/IExceptionNotifier.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.ExceptionHandling
{
public interface IExceptionNotifier
{
Task NotifyAsync([NotNull] ExceptionNotificationContext context);
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/ExceptionHandling/IExceptionSubscriber.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.ExceptionHandling
{
public interface IExceptionSubscriber
{
Task HandleAsync([NotNull] ExceptionNotificationContext context);
}
}

15
framework/src/Volo.Abp.Ldap/Volo/Abp/Ldap/LdapManager.cs

@ -3,7 +3,9 @@ using Microsoft.Extensions.Options;
using Novell.Directory.Ldap; using Novell.Directory.Ldap;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Ldap.Exceptions; using Volo.Abp.Ldap.Exceptions;
using Volo.Abp.Ldap.Modeling; using Volo.Abp.Ldap.Modeling;
@ -13,6 +15,7 @@ namespace Volo.Abp.Ldap
{ {
private readonly string _searchBase; private readonly string _searchBase;
private readonly AbpLdapOptions _ldapOptions; private readonly AbpLdapOptions _ldapOptions;
private readonly IHybridServiceScopeFactory _hybridServiceScopeFactory;
private readonly string[] _attributes = private readonly string[] _attributes =
{ {
@ -21,8 +24,9 @@ namespace Volo.Abp.Ldap
"sAMAccountName", "userPrincipalName", "telephoneNumber", "mail" "sAMAccountName", "userPrincipalName", "telephoneNumber", "mail"
}; };
public LdapManager(IOptions<AbpLdapOptions> ldapSettingsOptions) public LdapManager(IOptions<AbpLdapOptions> ldapSettingsOptions, IHybridServiceScopeFactory hybridServiceScopeFactory)
{ {
_hybridServiceScopeFactory = hybridServiceScopeFactory;
_ldapOptions = ldapSettingsOptions.Value; _ldapOptions = ldapSettingsOptions.Value;
_searchBase = _ldapOptions.SearchBase; _searchBase = _ldapOptions.SearchBase;
} }
@ -231,8 +235,15 @@ namespace Volo.Abp.Ldap
return true; return true;
} }
} }
catch (Exception ) catch (Exception ex)
{ {
using (var scope = _hybridServiceScopeFactory.CreateScope())
{
scope.ServiceProvider
.GetRequiredService<IExceptionNotifier>()
.NotifyAsync(ex);
}
return false; return false;
} }
} }

11
framework/src/Volo.Abp.RabbitMQ/Volo/Abp/RabbitMQ/RabbitMqMessageConsumer.cs

@ -7,6 +7,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.RabbitMQ namespace Volo.Abp.RabbitMQ
@ -17,6 +18,8 @@ namespace Volo.Abp.RabbitMQ
protected IConnectionPool ConnectionPool { get; } protected IConnectionPool ConnectionPool { get; }
protected IExceptionNotifier ExceptionNotifier { get; }
protected AbpTimer Timer { get; } protected AbpTimer Timer { get; }
protected ExchangeDeclareConfiguration Exchange { get; private set; } protected ExchangeDeclareConfiguration Exchange { get; private set; }
@ -35,10 +38,12 @@ namespace Volo.Abp.RabbitMQ
public RabbitMqMessageConsumer( public RabbitMqMessageConsumer(
IConnectionPool connectionPool, IConnectionPool connectionPool,
AbpTimer timer) AbpTimer timer,
IExceptionNotifier exceptionNotifier)
{ {
ConnectionPool = connectionPool; ConnectionPool = connectionPool;
Timer = timer; Timer = timer;
ExceptionNotifier = exceptionNotifier;
Logger = NullLogger<RabbitMqMessageConsumer>.Instance; Logger = NullLogger<RabbitMqMessageConsumer>.Instance;
QueueBindCommands = new ConcurrentQueue<QueueBindCommand>(); QueueBindCommands = new ConcurrentQueue<QueueBindCommand>();
@ -114,6 +119,7 @@ namespace Volo.Abp.RabbitMQ
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogException(ex, LogLevel.Warning); Logger.LogException(ex, LogLevel.Warning);
AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning));
} }
} }
@ -180,6 +186,7 @@ namespace Volo.Abp.RabbitMQ
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogException(ex, LogLevel.Warning); Logger.LogException(ex, LogLevel.Warning);
AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning));
} }
} }
@ -197,6 +204,7 @@ namespace Volo.Abp.RabbitMQ
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogException(ex); Logger.LogException(ex);
await ExceptionNotifier.NotifyAsync(ex);
} }
} }
@ -214,6 +222,7 @@ namespace Volo.Abp.RabbitMQ
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogException(ex, LogLevel.Warning); Logger.LogException(ex, LogLevel.Warning);
AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex, logLevel: LogLevel.Warning));
} }
} }

20
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs

@ -3,11 +3,12 @@ using System.Threading;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling;
namespace Volo.Abp.Threading namespace Volo.Abp.Threading
{ {
/// <summary> /// <summary>
/// A roboust timer implementation that ensures no overlapping occurs. It waits exactly specified <see cref="Period"/> between ticks. /// A robust timer implementation that ensures no overlapping occurs. It waits exactly specified <see cref="Period"/> between ticks.
/// </summary> /// </summary>
public class AbpTimer : ITransientDependency public class AbpTimer : ITransientDependency
{ {
@ -29,15 +30,23 @@ namespace Volo.Abp.Threading
public ILogger<AbpTimer> Logger { get; set; } public ILogger<AbpTimer> Logger { get; set; }
protected IExceptionNotifier ExceptionNotifier { get; }
private readonly Timer _taskTimer; private readonly Timer _taskTimer;
private volatile bool _performingTasks; private volatile bool _performingTasks;
private volatile bool _isRunning; private volatile bool _isRunning;
public AbpTimer() public AbpTimer(IExceptionNotifier exceptionNotifier)
{ {
ExceptionNotifier = exceptionNotifier;
Logger = NullLogger<AbpTimer>.Instance; Logger = NullLogger<AbpTimer>.Instance;
_taskTimer = new Timer(TimerCallBack, null, Timeout.Infinite, Timeout.Infinite); _taskTimer = new Timer(
TimerCallBack,
null,
Timeout.Infinite,
Timeout.Infinite
);
} }
public void Start(CancellationToken cancellationToken = default) public void Start(CancellationToken cancellationToken = default)
@ -89,9 +98,10 @@ namespace Volo.Abp.Threading
{ {
Elapsed.InvokeSafely(this, new EventArgs()); Elapsed.InvokeSafely(this, new EventArgs());
} }
catch catch(Exception ex)
{ {
Logger.LogException(ex);
AsyncHelper.RunSync(() => ExceptionNotifier.NotifyAsync(ex));
} }
finally finally
{ {

23
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs

@ -1,6 +1,10 @@
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NSubstitute;
using Shouldly; using Shouldly;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Http; using Volo.Abp.Http;
using Xunit; using Xunit;
@ -8,12 +12,27 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{ {
public class ExceptionTestController_Tests : AspNetCoreMvcTestBase public class ExceptionTestController_Tests : AspNetCoreMvcTestBase
{ {
private IExceptionSubscriber _fakeExceptionSubscriber;
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
base.ConfigureServices(context, services);
_fakeExceptionSubscriber = Substitute.For<IExceptionSubscriber>();
services.AddSingleton(_fakeExceptionSubscriber);
}
[Fact] [Fact]
public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value() public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value()
{ {
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/exception-test/UserFriendlyException1", HttpStatusCode.Forbidden); var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/exception-test/UserFriendlyException1", HttpStatusCode.Forbidden);
result.Error.ShouldNotBeNull(); result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe("This is a sample exception!"); result.Error.Message.ShouldBe("This is a sample exception!");
_fakeExceptionSubscriber
.Received()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
} }
[Fact] [Fact]
@ -24,6 +43,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
"/api/exception-test/UserFriendlyException2" "/api/exception-test/UserFriendlyException2"
) )
); );
_fakeExceptionSubscriber
.DidNotReceive()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
} }
} }
} }

Loading…
Cancel
Save